specfuse-loop 0.22.0__tar.gz → 0.22.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.
- {specfuse_loop-0.22.0/specfuse_loop.egg-info → specfuse_loop-0.22.1}/PKG-INFO +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/pyproject.toml +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/bugs.py +12 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/run.py +33 -5
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/agent_policy.py +40 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/bug_lane_run.py +52 -2
- specfuse_loop-0.22.1/specfuse/loop/data/VERSION +1 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/loop.py +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1/specfuse_loop.egg-info}/PKG-INFO +1 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/SOURCES.txt +1 -0
- specfuse_loop-0.22.1/tests/test_required_checks.py +161 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_run_level_failure_breaker.py +113 -0
- specfuse_loop-0.22.0/specfuse/loop/data/VERSION +0 -1
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/LICENSE +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/NOTICE +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/README.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/setup.cfg +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/diagnose_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/drafting_answers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/drafting_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/drafting_questions.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/driver_command.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/driver_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/monitoring_read.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/answers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/findings_autofix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/findings_diagnose.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/queue_read.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/repo_detect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/severity_backfill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/triage_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/worktree.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/_filelock.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/_miniyaml.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/_wu_sections.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/adopt_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/arm_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/arm_sweep.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/arm_txn.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/bug_lane.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/bug_lane_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/build_provenance.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/caller_check.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/changelog.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/closing_requirements.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/cost.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/criteria_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/LEARNINGS.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/getting-started.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/glossary.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/methodology.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/skills.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/gitignore.snippet +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/monitoring.yml.example +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/roadmap.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/close-discipline.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/correlation-ids.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/human-output.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/never-touch.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/operator-escalation.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/planning-discipline.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/result-contract.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/security-boundaries.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/verification-discipline.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules-local/README.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/event.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/GATE.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/PLAN.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/WU.template.md +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/verification.yml.example +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/decisions_format.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/driver_edit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/events_stats.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gate_commands.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gate_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gh_backend.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gh_features.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/heartbeat.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/judge.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/labels.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/learnings_distil.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/learnings_query.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_closing.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_monitoring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_plan.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_roadmap.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/notify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/notify_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/notify_sla.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/plan_baseline.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/policy_proposals.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/policy_review.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/prerun.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/prerun_capture.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/promotion.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/rearm_migration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/replay_spin.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/roadmap_archive.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/scaffold.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/validate_event.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/adapters.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/artifact.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/diagnose_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/diagnosis.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/fingerprint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/issues.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/__init__.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/_azure_auth.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/azure_app_insights.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/azure_service_bus.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/schedule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/dependency_links.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/entry_points.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/requires.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/top_level.txt +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_abandon_feature_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_abandoned_work_reported.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_abandoned_wu_strands_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_adopt_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_bug_selection_dedup.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_default_features_root.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_driver_command.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_driver_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_driver_restart.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_escalation_on_issue.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_escalation_run_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_findings_seam.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_invoke_timeout.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_invoke_usage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_item_worktree.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_lock.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_key_ownership.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_queue.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_schema.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_triage_dial.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_answers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_bugs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_findings_autofix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_findings_diagnose.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_isolation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_queue_read.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_relays_driver_output.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_repo_detect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_run_progress.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_seam.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_snapshot_refresh.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_answer_escalation_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_app_insights_adapters.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_archive_ref_reconciliation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_eval_lint_class.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_eval_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_gate_edits_uncommitted.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_sweep.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_sweep_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_txn.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_artifact_redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attempt_note_untracked_files.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attempt_outcome_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attempt_outcome_emission.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attention_nonwriting_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attention_silence_section.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attention_skill_structure.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autoclose_deferral_visibility.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autoclose_gate_note.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autoclose_stub_states_what_passed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_live.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_predicate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_consent.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_firstrun.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_no_cwd_leak.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_plugin.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_azure_credential_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_backend.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_escalation_message.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_persistence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_probe.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_provenance.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_tree_hash_key.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bash_routing.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bats_suites_gated.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binary_verdict.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binding_block_advisory.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binding_block_allocation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binding_block_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bookkeeping_commit_crash_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bookkeeping_no_verify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_brief_covers_every_unit_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_ci_pending.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_guardrails.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_label_nonfatal.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_labels_registered.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_min_severity.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_policy_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_pr_number_carried.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_preflight_and_dial.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_test_paths.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_build_provenance.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_caller_check_ratchet.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changed_file_test_selection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_append_only.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_collection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_release_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_schema.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ci_conclusion_wait.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ci_runs_every_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_claude_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_close_b_pending_learnings.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_close_records_feature_oracle.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_close_wu_self_reference.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_deliverable_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_guard_contracts.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_guard_prediction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_learnings_destination.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_requirements.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_coauthor_trailer_allowlist.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_conditional_reflection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_console_encoding.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_convergent_iteration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_convergent_wu_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_correlation_id_override.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_correlation_id_two_surfaces.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_cost_lifetime.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_cost_tracking.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_criteria_per_gate_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_criteria_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_criteria_worklist.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decision_citation_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decision_followups_discharged.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decision_override_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decisions_format.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_default_severity_aliases.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_degraded_oracle.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_deliverable_missing_note.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_deliverable_presence_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_agent_policy_review_mode.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_agent_policy_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_monitoring_discovery.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_monitoring_skill_registration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_design_for_diagnosis_rule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_deterministic_refusal_repeat.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnose_headless.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnose_issue_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnosis.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnosis_roundtrip_live.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_dispatch_skeleton.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_distilled_accept_step.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_doctor.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_doctor_command_preflight.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_done_feature_gates.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_draft_feature_answers_mode.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_answer_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_invoke.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_question_issue.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_questions.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_reply_shape.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_edit_detection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_event_registry_covers_emitters.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_lock.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_module_surface.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_restart_halt_surfaced.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_restart_halt_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_restart_hold.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_staleness_gate_summary.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_staleness_warning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_dry_run_no_branch_mutation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_duration_tracking.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_empty_files_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ensure_feature_branch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ensure_feature_branch_base.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_assignee_policy.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_crash_containment.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_dedup_search.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_emit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_event_gate_full_envelope.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_events_redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_events_stats.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_extra_gates.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_failure_artifact_model.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_failure_signature_fail_prefix.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_failure_signature_skips_log_noise.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_branch_auto_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_branch_squash_merge.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_branch_stale_base.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_id_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_oracle_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_provider_drafting_dispatch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_review_accumulation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_filelock_portable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_files_changed_auto_repair.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_find_pr_no_search_index.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fingerprint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fix_bug_diff_self_check.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fix_bug_headless.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fix_bug_reads_comments.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fold_contract_documented.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_followups_artifact.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_force_full_close.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_frontmatter_scalar_quoting.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_broad_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_budget_post_dispatch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_commands_needs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_calibration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_intermediate_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_repo_flag.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_terminal_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_report_verdict.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gh_backend.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gh_features.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_git_env_isolation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gitleaks_pinning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_groom_backlog_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_refusal_failure_class.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_refusal_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_repair_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_repair_prompt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_hashed_denylist.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_hashed_denylist_ci.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_heartbeat.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_heartbeat_adapter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_human_output_rule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_human_work_unit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_hyphenated_gate_name.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_init_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_installed_copy_driver_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_interpreter_normalization.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_close_path.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_cost.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_cost_fold_committed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_module.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_path_registry.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_terminal_flip_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_label_provisioning_runner_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_label_registry.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_label_registry_covers_consumers.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_language_aware_narrow_selection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lazy_baseline_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_leak_findings_redaction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_leak_scan.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_leak_scan_content.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_curate_vendored_promote.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_distil_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_query.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_staging.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_suggest_signature_filter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_weights.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_legacy_4wu_terminal_flips.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lifecycle_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lifetime_accounting.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_ac_observable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_bare_produces_path.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_boundary_consistency.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_boundary_extraction.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_close_intermediate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_close_verdict_not_predecided.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_close_wu.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_closing.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_closing_criteria.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_closing_criteria_pristine.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_correlation_id.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_covers_load_wu_invariants.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_feature_oracle_declared.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_gate_proportionality.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_monitoring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_oracle_env.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_contract_fields.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_errors.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_next_draft.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_relative_import.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_verdict_exempt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_planning_sections.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_produces_driver_helper.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_produces_satisfiability.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_roadmap.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_sections.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_task_graph_yaml_selection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_verification_reference.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_live_tests_opt_in.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_load_bearing_close_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_attempt_evidence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_auto_archive.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_caveman_preamble.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_close_intermediate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_criteria_skeleton.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_criteria_survival.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_defaults_by_type.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_effort.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_failure_note_cap.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_files_changed_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_gate_budget.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_gate_needs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_model_alias.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_orchestration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_post_pass_invariant.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_reset_preserving_events.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_smoke_runner.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_unsandboxed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_worklist_injection.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_zero_token_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_make_repo_gpgsign_isolation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_marker_dual_shape.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_maven_failure_excerpt.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_max_attempts_configurable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_max_open_prs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_migrate_legacy.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_miniyaml_equivalence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_miniyaml_negative.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitor_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitor_issue_lifecycle.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitor_runner_surfaces.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_cron_dialect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_example.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_fenced_blocks.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_seed.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_narrow_fallback_warning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_no_pytest_subprocess.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_notify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_notify_escalation.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_notify_sla.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_operator_escalation_rule.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_oracle_set_declared.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_oracle_unrunnable_attribution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pick_feature_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pin_honesty_and_integrity.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pin_marker_not_inherited.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pin_marker_spawn_sites.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_plan_baseline.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_planned_cost_lint.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_planning_cost_floor.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_plugin_qualified_dispatch.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_policy_budgets_applied.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_policy_proposals.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_policy_review.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pr_checks_json_field.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pr_reference_in_title.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prep_halt_operator_surface.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prepare_feature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prepare_scaffold_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prerun_capture.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prerun_oracles.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prerun_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produced_document_verdict_guard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_bracket_literal.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_bracket_wildcard.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_field.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_in_diff.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_justification.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_recursive_glob.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_semantics_unified.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_shape.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_progress_forward_note.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_progress_lines_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_promotion_ledger.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_provider_escalation_traces.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_provision_labels.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_queue_stalled_adapter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rearm_fold_marker.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rearm_migration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_red_baseline_never_reused.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_redaction_windows_home.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_reflection_required_signals.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_event_emission.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_note_collision.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_trigger.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_turn_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replay_spin.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_resolve_base.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_restart_cap_counts_progress.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_result_block.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_result_block_audience.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_resume_command_build.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_retain_on_guard_refusal_docs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_add_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_cli.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_shim.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_skill.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_skill_delegates.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_link_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_row_parser.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ruff_verdict_lines.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rule_import_convergence.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_data_in_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_doc_hygiene.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_docs.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_init.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_label_provisioning.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_manifest.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_resources.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_seed_sanity.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_skew_halts.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_upgrade.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_wiring.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_schedule_dialect.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_seed_documents_prerun_keys.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_service_bus_dlq_adapter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_session_env_preflight.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_aliases.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_apply.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_limit.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_marker.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_run.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_rubric.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_shipped_templates_parse.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_skill_discovery_links.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_skills_vendored_in_sync.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_spinning_rearm_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_spinout_brief_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_spinout_brief_replan_option.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_squash_commit_hook_crash.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_stop_rationale_captured.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_stranded_halt_cause.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_surefire_run_level_signature.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_surefire_verdict_lines.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_template_closing_shapes.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_terminal_flip_ownership.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_terminal_flips.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_terminal_gate_message.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_tiered_verification_e2e.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_timeout_kill_portable.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_apply.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_severity_classify.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_severity_end_to_end.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_severity_write.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_skill_contract.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_skips_agent_escalations.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_tsc_verdict_lines.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_umbrella_version_resolution.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_unterminated_frontmatter.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_untracked_feature_folder.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_untracked_file_absorption.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_upgrade_integration.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_upgrade_merge_gate.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_validate_event.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_validate_event_driver_types.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_verdict_coupling.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_verify_empty_gate_set.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_version_consistency.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_version_skew.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_windows_gate_exec.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_worktree_restore.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_wu_execution_metadata.py +0 -0
- {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_wu_template_renders_lintable.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfuse-loop"
|
|
7
|
-
version = "0.22.
|
|
7
|
+
version = "0.22.1"
|
|
8
8
|
description = "Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -788,7 +788,18 @@ class BugsProvider:
|
|
|
788
788
|
label_written=result.label_written,
|
|
789
789
|
)
|
|
790
790
|
|
|
791
|
-
|
|
791
|
+
# #3372: only a session that produced NO outcome marker can have an
|
|
792
|
+
# environmental cause — a dispatch that failed, an empty result, a
|
|
793
|
+
# crash. `outcome_named` is exactly that distinction, already carried
|
|
794
|
+
# from the session's own text (#3343). A `refused` is `/fix-bug`
|
|
795
|
+
# reading the issue and judging it out of scope; three of those say the
|
|
796
|
+
# queue holds three feature-scoped issues, not that anything is broken.
|
|
797
|
+
return ActionOutcome(
|
|
798
|
+
status=STATUS_ESCALATED,
|
|
799
|
+
detail=detail,
|
|
800
|
+
escalation=escalation,
|
|
801
|
+
environmental=not result.outcome_named,
|
|
802
|
+
)
|
|
792
803
|
|
|
793
804
|
def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
|
|
794
805
|
return None
|
|
@@ -220,6 +220,24 @@ class ActionOutcome:
|
|
|
220
220
|
#: same shape as `NON_JUDGE_MODULES` and
|
|
221
221
|
#: `DEPENDENCY_MANIFEST_NAMED_UNCOVERED`.
|
|
222
222
|
escalation_waived: str = ""
|
|
223
|
+
#: Whether this escalation could have an environmental cause (#3372).
|
|
224
|
+
#:
|
|
225
|
+
#: Only an outcome reached WITHOUT reading the item can be about the run's
|
|
226
|
+
#: environment: a dispatch that failed, a session that produced no outcome
|
|
227
|
+
#: marker, a crash. An outcome reached BY reading the item is a judgement
|
|
228
|
+
#: about that item — `/fix-bug`'s `refused` says the work is not bug-sized,
|
|
229
|
+
#: `ci_not_green` and `pr_not_found` are facts about that issue's PR — and
|
|
230
|
+
#: three in a row says the queue holds three such issues, not that anything
|
|
231
|
+
#: is broken.
|
|
232
|
+
#:
|
|
233
|
+
#: #3343's breaker counted every escalation and stopped a run that was
|
|
234
|
+
#: working: two fixes merged, one held on red CI, six correctly declined,
|
|
235
|
+
#: 19 items never reached, and an escalation asserting an environment
|
|
236
|
+
#: cause a human then had to disprove.
|
|
237
|
+
#:
|
|
238
|
+
#: **Defaults False so a provider must opt in.** A provider that says
|
|
239
|
+
#: nothing never trips the breaker, rather than tripping it by omission.
|
|
240
|
+
environmental: bool = False
|
|
223
241
|
|
|
224
242
|
|
|
225
243
|
class ActionProvider(Protocol):
|
|
@@ -1003,12 +1021,22 @@ def run_agent(
|
|
|
1003
1021
|
# #3343: an escalation whose cause keeps repeating is not about
|
|
1004
1022
|
# the item. Counted here rather than in a provider, because only
|
|
1005
1023
|
# the run can see that the SAME thing failed N times.
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1024
|
+
#
|
|
1025
|
+
# #3372: but only an outcome the provider marks `environmental`
|
|
1026
|
+
# can have an environmental cause. A per-item judgement —
|
|
1027
|
+
# `refused`, `ci_not_green`, `pr_not_found` — RESETS the count
|
|
1028
|
+
# rather than being ignored: a lane still reaching verdicts on
|
|
1029
|
+
# items is not one whose environment is broken.
|
|
1030
|
+
if not outcome.environmental:
|
|
1031
|
+
last_failure_signature = None
|
|
1032
|
+
consecutive_identical_failures = 0
|
|
1009
1033
|
else:
|
|
1010
|
-
|
|
1011
|
-
|
|
1034
|
+
signature = failure_signature(item, outcome)
|
|
1035
|
+
if signature == last_failure_signature:
|
|
1036
|
+
consecutive_identical_failures += 1
|
|
1037
|
+
else:
|
|
1038
|
+
last_failure_signature = signature
|
|
1039
|
+
consecutive_identical_failures = 1
|
|
1012
1040
|
|
|
1013
1041
|
if consecutive_identical_failures >= IDENTICAL_FAILURE_LIMIT:
|
|
1014
1042
|
run_reason = (
|
|
@@ -46,6 +46,7 @@ __all__ = (
|
|
|
46
46
|
"SEVERITY_VALUES",
|
|
47
47
|
"read_severity_label",
|
|
48
48
|
"resolve_severity_aliases",
|
|
49
|
+
"resolve_required_checks",
|
|
49
50
|
"DEFAULT_SEVERITY_ALIASES",
|
|
50
51
|
"AUTOMERGE_VALUES",
|
|
51
52
|
"GATE_REVIEW_VALUES",
|
|
@@ -295,6 +296,45 @@ def resolve_max_items(flag: "int | None",
|
|
|
295
296
|
return _positive_int_budget(path, "max_items_per_day")
|
|
296
297
|
|
|
297
298
|
|
|
299
|
+
def resolve_required_checks(path: "str | Path | None" = None) -> tuple:
|
|
300
|
+
"""`rules.bugs.required_checks` as a tuple of check names, or `()` (#3373).
|
|
301
|
+
|
|
302
|
+
The names that must be **present and passing** before the bug lane may
|
|
303
|
+
auto-merge. Empty when absent or unusable, so a deployment that never
|
|
304
|
+
declared it behaves exactly as before: any `success` conclusion is accepted.
|
|
305
|
+
|
|
306
|
+
Why it exists. `evaluate_merge_guardrails` accepted any `success`, and a
|
|
307
|
+
repository whose PR job is a deliberate fast lane — excluding a test group
|
|
308
|
+
that guards committed expectations — gave a `success` that **could not
|
|
309
|
+
fail** on that class of defect. A bug-lane PR merged under `automerge: "on"`
|
|
310
|
+
and left `main` red for an hour.
|
|
311
|
+
|
|
312
|
+
The split those repositories rely on assumes every change already passed
|
|
313
|
+
the full gate set before its PR opened. That holds for driver work-unit
|
|
314
|
+
PRs and does **not** hold for bug-lane PRs, where a headless session
|
|
315
|
+
chooses which tests to run. This key is how a repository points the lane at
|
|
316
|
+
the complete check rather than the fast one.
|
|
317
|
+
|
|
318
|
+
A string is accepted as a one-element list — the common single-check case —
|
|
319
|
+
and anything else unusable resolves to `()`. Requiring nothing is the
|
|
320
|
+
pre-existing behaviour; inventing a requirement from a malformed value
|
|
321
|
+
would block every merge on a typo.
|
|
322
|
+
"""
|
|
323
|
+
try:
|
|
324
|
+
policy = load_policy(path)
|
|
325
|
+
except (FileNotFoundError, OSError):
|
|
326
|
+
return ()
|
|
327
|
+
rules = policy.get("rules") if isinstance(policy, dict) else None
|
|
328
|
+
bugs = rules.get("bugs") if isinstance(rules, dict) else None
|
|
329
|
+
raw = bugs.get("required_checks") if isinstance(bugs, dict) else None
|
|
330
|
+
if isinstance(raw, str):
|
|
331
|
+
name = raw.strip()
|
|
332
|
+
return (name,) if name else ()
|
|
333
|
+
if not isinstance(raw, list):
|
|
334
|
+
return ()
|
|
335
|
+
return tuple(str(n).strip() for n in raw if str(n).strip())
|
|
336
|
+
|
|
337
|
+
|
|
298
338
|
def resolve_max_open_prs(path: "str | Path | None" = None) -> "int | None":
|
|
299
339
|
"""`budgets.max_open_prs`, or None when unset or unusable (#3340).
|
|
300
340
|
|
|
@@ -38,6 +38,7 @@ from typing import Any, Callable, Optional
|
|
|
38
38
|
|
|
39
39
|
from specfuse.loop.agent_policy import (
|
|
40
40
|
bug_lane_ci_wait_seconds,
|
|
41
|
+
resolve_required_checks,
|
|
41
42
|
bug_lane_limits,
|
|
42
43
|
resolve_bug_automerge,
|
|
43
44
|
)
|
|
@@ -197,7 +198,40 @@ _BUCKET_PENDING = "pending"
|
|
|
197
198
|
_CI_FAILING = "fail"
|
|
198
199
|
|
|
199
200
|
|
|
200
|
-
def
|
|
201
|
+
def _required_checks_verdict(rows: list, required) -> str:
|
|
202
|
+
"""`""` when every required check is present and passing, else why not.
|
|
203
|
+
|
|
204
|
+
A required check that **never ran** is the case #3373 is about: the
|
|
205
|
+
repository's PR job was green and the check that would have caught the
|
|
206
|
+
defect was not in the list at all. It is not a failure — nothing failed —
|
|
207
|
+
and it is equally not the green the operator asked the lane to require.
|
|
208
|
+
|
|
209
|
+
A required check that was **skipped** is treated the same way. Skipping is
|
|
210
|
+
not a failure in general, which is why `_BUCKETS_OK` tolerates it, but a
|
|
211
|
+
check an operator named as required and that did not run cannot establish
|
|
212
|
+
what it was required to establish.
|
|
213
|
+
"""
|
|
214
|
+
if not required:
|
|
215
|
+
return ""
|
|
216
|
+
by_name = {}
|
|
217
|
+
for row in rows:
|
|
218
|
+
name = row.get("name")
|
|
219
|
+
if isinstance(name, str):
|
|
220
|
+
by_name[name] = str(row.get("bucket") or "").lower()
|
|
221
|
+
for name in required:
|
|
222
|
+
bucket = by_name.get(name)
|
|
223
|
+
if bucket is None:
|
|
224
|
+
return f"required check never ran: {name}"
|
|
225
|
+
if bucket == _BUCKET_PENDING:
|
|
226
|
+
return _CI_PENDING
|
|
227
|
+
if bucket != "pass":
|
|
228
|
+
return f"required check not passing: {name} ({bucket})"
|
|
229
|
+
return ""
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _read_ci_conclusion_once(
|
|
233
|
+
runner: Callable, repo: str, pr_number: int, required=()
|
|
234
|
+
) -> str:
|
|
201
235
|
"""One `gh pr checks` read, mapped through `bucket`.
|
|
202
236
|
|
|
203
237
|
`_CI_PENDING` when any check is queued or running, or when no check is
|
|
@@ -244,6 +278,14 @@ def _read_ci_conclusion_once(runner: Callable, repo: str, pr_number: int) -> str
|
|
|
244
278
|
|
|
245
279
|
if _BUCKET_PENDING in buckets:
|
|
246
280
|
return _CI_PENDING
|
|
281
|
+
|
|
282
|
+
# #3373: the operator's own list, checked before the aggregate verdict.
|
|
283
|
+
# A repository whose PR job deliberately excludes a test group can point
|
|
284
|
+
# the lane at the complete check instead of the fast one.
|
|
285
|
+
verdict = _required_checks_verdict(rows, required)
|
|
286
|
+
if verdict:
|
|
287
|
+
return verdict
|
|
288
|
+
|
|
247
289
|
if buckets <= _BUCKETS_OK:
|
|
248
290
|
return "success"
|
|
249
291
|
return _CI_FAILING
|
|
@@ -258,6 +300,7 @@ def pr_ci_conclusion(
|
|
|
258
300
|
clock: Callable = time.monotonic,
|
|
259
301
|
deadline_seconds: float = CI_WAIT_SECONDS,
|
|
260
302
|
poll_seconds: float = CI_POLL_SECONDS,
|
|
303
|
+
required_checks=(),
|
|
261
304
|
) -> str:
|
|
262
305
|
"""Wait for `pr_number`'s CI to reach a conclusion, then return it.
|
|
263
306
|
|
|
@@ -284,7 +327,9 @@ def pr_ci_conclusion(
|
|
|
284
327
|
"""
|
|
285
328
|
started = clock()
|
|
286
329
|
while True:
|
|
287
|
-
conclusion = _read_ci_conclusion_once(
|
|
330
|
+
conclusion = _read_ci_conclusion_once(
|
|
331
|
+
runner, repo, pr_number, required_checks
|
|
332
|
+
)
|
|
288
333
|
if conclusion != _CI_PENDING:
|
|
289
334
|
return conclusion
|
|
290
335
|
if clock() - started >= deadline_seconds:
|
|
@@ -720,6 +765,11 @@ def run_bug_lane(
|
|
|
720
765
|
ci_conclusion = pr_ci_conclusion(
|
|
721
766
|
runner, repo, pr_number,
|
|
722
767
|
sleep=ci_sleep, clock=ci_clock, deadline_seconds=deadline_seconds,
|
|
768
|
+
# #3373: the checks this repository says must be green before the lane
|
|
769
|
+
# may merge unattended. Empty unless declared, so behaviour is
|
|
770
|
+
# unchanged for anyone who has not pointed the lane at a complete
|
|
771
|
+
# check.
|
|
772
|
+
required_checks=resolve_required_checks(policy_path),
|
|
723
773
|
)
|
|
724
774
|
state_reader = GitHubMergeCapState(runner=runner, repo=repo, now=now)
|
|
725
775
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.22.1
|
|
@@ -125,7 +125,7 @@ SPECFUSE_DIR = Path(".specfuse")
|
|
|
125
125
|
REPO_ROOT = SPECFUSE_DIR.parent
|
|
126
126
|
FEATURES_DIR = SPECFUSE_DIR / "features"
|
|
127
127
|
VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
|
|
128
|
-
DRIVER_VERSION = "0.22.
|
|
128
|
+
DRIVER_VERSION = "0.22.1"
|
|
129
129
|
# Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
|
|
130
130
|
# version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
|
|
131
131
|
# the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
|
|
@@ -516,6 +516,7 @@ tests/test_replan_note_collision.py
|
|
|
516
516
|
tests/test_replan_trigger.py
|
|
517
517
|
tests/test_replan_turn_contract.py
|
|
518
518
|
tests/test_replay_spin.py
|
|
519
|
+
tests/test_required_checks.py
|
|
519
520
|
tests/test_resolve_base.py
|
|
520
521
|
tests/test_restart_cap_counts_progress.py
|
|
521
522
|
tests/test_result_block.py
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Copyright 2026 Specfuse contributors
|
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE.
|
|
3
|
+
"""`rules.bugs.required_checks` — the lane states what green it requires (#3373).
|
|
4
|
+
|
|
5
|
+
`automerge` merged a PR that left `main` red for an hour. All six guardrails
|
|
6
|
+
passed and `ci_conclusion` was `success`, because that repository's PR job is
|
|
7
|
+
deliberately a fast lane excluding the `regression` group — and both tests the
|
|
8
|
+
fix broke carry `@Tag("regression")`. The check the lane read **could not
|
|
9
|
+
fail** on that class of defect.
|
|
10
|
+
|
|
11
|
+
The nightly's own header states the assumption the split rests on: *"every
|
|
12
|
+
change had already passed it in the loop's `code` gate before its PR was
|
|
13
|
+
opened"*. True for driver work-unit PRs. **Not** true for bug-lane PRs, where a
|
|
14
|
+
headless session chooses which tests to run and reports the ones it chose.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import textwrap
|
|
21
|
+
import unittest
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from tempfile import TemporaryDirectory
|
|
24
|
+
from types import SimpleNamespace
|
|
25
|
+
|
|
26
|
+
from specfuse.loop.agent_policy import resolve_required_checks
|
|
27
|
+
from specfuse.loop.bug_lane_run import pr_ci_conclusion
|
|
28
|
+
|
|
29
|
+
_BASE = """\
|
|
30
|
+
version: 1
|
|
31
|
+
queue: []
|
|
32
|
+
rules:
|
|
33
|
+
bugs:
|
|
34
|
+
preempt: true
|
|
35
|
+
min_severity: medium
|
|
36
|
+
automerge: "on"
|
|
37
|
+
{extra} features:
|
|
38
|
+
gate_review: human
|
|
39
|
+
wip_limit: 1
|
|
40
|
+
triage:
|
|
41
|
+
auto: false
|
|
42
|
+
budgets:
|
|
43
|
+
max_tokens_per_run: 100000
|
|
44
|
+
max_open_prs: 5
|
|
45
|
+
max_items_per_day: 10
|
|
46
|
+
escalation:
|
|
47
|
+
provider: none
|
|
48
|
+
webhook_env: ""
|
|
49
|
+
assignee: ""
|
|
50
|
+
quiet_hours: ""
|
|
51
|
+
sla_hours: 24
|
|
52
|
+
silence_hours: 24
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _runner_for(rows):
|
|
57
|
+
def runner(argv, check=False):
|
|
58
|
+
if argv[:3] == ["gh", "pr", "checks"]:
|
|
59
|
+
return SimpleNamespace(returncode=0, stdout=json.dumps(rows), stderr="")
|
|
60
|
+
return SimpleNamespace(returncode=0, stdout="", stderr="")
|
|
61
|
+
return runner
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _conclusion(rows, required=()):
|
|
65
|
+
return pr_ci_conclusion(
|
|
66
|
+
_runner_for(rows), "acme/widget", 7,
|
|
67
|
+
sleep=lambda _s: None, clock=lambda: 0.0,
|
|
68
|
+
deadline_seconds=0.0, poll_seconds=0.0,
|
|
69
|
+
required_checks=required,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ResolvingTheKey(unittest.TestCase):
|
|
74
|
+
|
|
75
|
+
def setUp(self):
|
|
76
|
+
self._tmp = TemporaryDirectory()
|
|
77
|
+
self.addCleanup(self._tmp.cleanup)
|
|
78
|
+
|
|
79
|
+
def _write(self, extra: str = "") -> str:
|
|
80
|
+
p = Path(self._tmp.name) / "agent-policy.yml"
|
|
81
|
+
p.write_text(_BASE.format(extra=extra), encoding="utf-8")
|
|
82
|
+
return str(p)
|
|
83
|
+
|
|
84
|
+
def test_absent_key_requires_nothing(self):
|
|
85
|
+
self.assertEqual(resolve_required_checks(self._write()), ())
|
|
86
|
+
|
|
87
|
+
def test_a_declared_list_resolves(self):
|
|
88
|
+
block = textwrap.indent("required_checks:\n - full-suite\n - nightly\n", " ")
|
|
89
|
+
self.assertEqual(resolve_required_checks(self._write(block)), ("full-suite", "nightly"))
|
|
90
|
+
|
|
91
|
+
def test_a_string_is_the_single_check_case(self):
|
|
92
|
+
block = textwrap.indent("required_checks: full-suite\n", " ")
|
|
93
|
+
self.assertEqual(resolve_required_checks(self._write(block)), ("full-suite",))
|
|
94
|
+
|
|
95
|
+
def test_a_malformed_value_requires_nothing_rather_than_blocking_everything(self):
|
|
96
|
+
block = textwrap.indent("required_checks: 7\n", " ")
|
|
97
|
+
self.assertEqual(resolve_required_checks(self._write(block)), ())
|
|
98
|
+
|
|
99
|
+
def test_a_missing_file_requires_nothing(self):
|
|
100
|
+
self.assertEqual(resolve_required_checks("/nonexistent/agent-policy.yml"), ())
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TheGreenTheLaneReads(unittest.TestCase):
|
|
104
|
+
|
|
105
|
+
def test_without_required_checks_behaviour_is_unchanged(self):
|
|
106
|
+
rows = [{"bucket": "pass", "name": "build", "state": "SUCCESS"}]
|
|
107
|
+
self.assertEqual(_conclusion(rows), "success")
|
|
108
|
+
|
|
109
|
+
def test_a_required_check_that_passed_is_success(self):
|
|
110
|
+
rows = [
|
|
111
|
+
{"bucket": "pass", "name": "build", "state": "SUCCESS"},
|
|
112
|
+
{"bucket": "pass", "name": "full-suite", "state": "SUCCESS"},
|
|
113
|
+
]
|
|
114
|
+
self.assertEqual(_conclusion(rows, ("full-suite",)), "success")
|
|
115
|
+
|
|
116
|
+
def test_a_required_check_that_never_ran_is_not_success(self):
|
|
117
|
+
# THE MEASURED CASE: the PR job is green and the check that would have
|
|
118
|
+
# caught the defect is not in the list at all.
|
|
119
|
+
rows = [{"bucket": "pass", "name": "build", "state": "SUCCESS"}]
|
|
120
|
+
got = _conclusion(rows, ("full-suite",))
|
|
121
|
+
self.assertNotEqual(got, "success")
|
|
122
|
+
self.assertIn("full-suite", got)
|
|
123
|
+
|
|
124
|
+
def test_a_required_check_that_failed_is_not_success(self):
|
|
125
|
+
rows = [
|
|
126
|
+
{"bucket": "pass", "name": "build", "state": "SUCCESS"},
|
|
127
|
+
{"bucket": "fail", "name": "full-suite", "state": "FAILURE"},
|
|
128
|
+
]
|
|
129
|
+
self.assertNotEqual(_conclusion(rows, ("full-suite",)), "success")
|
|
130
|
+
|
|
131
|
+
def test_a_required_check_that_was_skipped_is_not_success(self):
|
|
132
|
+
# Skipping is not a failure in general, which is why _BUCKETS_OK
|
|
133
|
+
# tolerates it — but a check an operator NAMED as required and that did
|
|
134
|
+
# not run cannot establish what it was required to establish.
|
|
135
|
+
rows = [
|
|
136
|
+
{"bucket": "pass", "name": "build", "state": "SUCCESS"},
|
|
137
|
+
{"bucket": "skipping", "name": "full-suite", "state": "SKIPPED"},
|
|
138
|
+
]
|
|
139
|
+
got = _conclusion(rows, ("full-suite",))
|
|
140
|
+
self.assertNotEqual(got, "success")
|
|
141
|
+
self.assertIn("full-suite", got)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class TheDeclineIsStillADecline(unittest.TestCase):
|
|
145
|
+
|
|
146
|
+
def test_a_non_success_conclusion_declines_the_merge(self):
|
|
147
|
+
from specfuse.loop.bug_lane import REASON_CI_NOT_GREEN, evaluate_merge_guardrails
|
|
148
|
+
|
|
149
|
+
decision = evaluate_merge_guardrails(
|
|
150
|
+
changed_files=["src/a.py", "tests/test_a.py"],
|
|
151
|
+
ci_conclusion="required check never ran: full-suite",
|
|
152
|
+
diff_lines=10, max_diff_lines=400,
|
|
153
|
+
provenance="issue", max_merges_per_day=3,
|
|
154
|
+
state_reader=lambda: 0,
|
|
155
|
+
)
|
|
156
|
+
self.assertFalse(decision.eligible)
|
|
157
|
+
self.assertEqual(decision.reason, REASON_CI_NOT_GREEN)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__": # pragma: no cover
|
|
161
|
+
unittest.main()
|
|
@@ -62,6 +62,10 @@ class _AlwaysFailsTheSameWay:
|
|
|
62
62
|
status=STATUS_ESCALATED,
|
|
63
63
|
detail=self._template.format(n=number),
|
|
64
64
|
escalation=None,
|
|
65
|
+
# The #3342 scenario these tests model — a dispatched command that
|
|
66
|
+
# did not resolve — is environmental, and since #3372 a provider
|
|
67
|
+
# must say so for the breaker to count it.
|
|
68
|
+
environmental=True,
|
|
65
69
|
escalation_waived="test double: the escalation trace is not this test's subject",
|
|
66
70
|
)
|
|
67
71
|
|
|
@@ -150,6 +154,7 @@ class TheBreakerStopsTheRun(unittest.TestCase):
|
|
|
150
154
|
return ActionOutcome(
|
|
151
155
|
status=STATUS_ESCALATED,
|
|
152
156
|
detail=f"could_not_proceed: {words[len(self.executed) - 1]}",
|
|
157
|
+
environmental=True,
|
|
153
158
|
escalation_waived="test double",
|
|
154
159
|
)
|
|
155
160
|
|
|
@@ -210,3 +215,111 @@ class TheTemplateStopsAssertingARefusal(unittest.TestCase):
|
|
|
210
215
|
"the promote-to-feature advice must not appear when the skill "
|
|
211
216
|
"never ran — that is what would have promoted 55 bugs",
|
|
212
217
|
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class OnlyEnvironmentalFailuresCount(unittest.TestCase):
|
|
221
|
+
"""#3372: the breaker counted `refused`, which is a per-item judgement.
|
|
222
|
+
|
|
223
|
+
`/fix-bug` refuses when it reads an issue and decides the work is not
|
|
224
|
+
bug-sized — SKILL.md: "Refuses if the work is large/complex/risky and
|
|
225
|
+
proposes promoting to a feature instead". Three in a row means the queue
|
|
226
|
+
holds three feature-scoped issues, which is unremarkable in a backlog.
|
|
227
|
+
|
|
228
|
+
Measured: a run worked 10 of 29 items — two fixes merged, one held on red
|
|
229
|
+
CI, six declined — and the breaker stopped the remaining 19, reporting
|
|
230
|
+
"the cause is this run's environment" about a lane that was working.
|
|
231
|
+
|
|
232
|
+
The distinction #3343 failed to draw: some outcomes are reached WITHOUT
|
|
233
|
+
reading the item (dispatch failed, no session ran) and some are reached BY
|
|
234
|
+
reading it. Only the first can be environmental.
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
def _provider(self, outcomes):
|
|
238
|
+
"""outcomes: list of (detail, environmental)."""
|
|
239
|
+
seq = list(outcomes)
|
|
240
|
+
|
|
241
|
+
class _Scripted:
|
|
242
|
+
def __init__(self):
|
|
243
|
+
self.executed = []
|
|
244
|
+
self._items = [
|
|
245
|
+
ActionItem(item_id=f"bug-{i}", kind="bug", summary="", queue_key=None)
|
|
246
|
+
for i in range(1, len(seq) + 1)
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
def advertise(self, snapshot):
|
|
250
|
+
return tuple(i for i in self._items if i.item_id not in self.executed)
|
|
251
|
+
|
|
252
|
+
def execute(self, item):
|
|
253
|
+
detail, env = seq[len(self.executed)]
|
|
254
|
+
self.executed.append(item.item_id)
|
|
255
|
+
return ActionOutcome(
|
|
256
|
+
status=STATUS_ESCALATED, detail=detail,
|
|
257
|
+
environmental=env,
|
|
258
|
+
escalation_waived="test double",
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def reconcile(self, item, outcome):
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
return _Scripted()
|
|
265
|
+
|
|
266
|
+
def test_consecutive_refusals_never_stop_the_run(self):
|
|
267
|
+
provider = self._provider([("refused", False)] * 6)
|
|
268
|
+
summary = _run(provider, [])
|
|
269
|
+
self.assertEqual(len(provider.executed), 6)
|
|
270
|
+
self.assertNotEqual(summary.stop_reason, STOP_RUN_LEVEL_FAILURE)
|
|
271
|
+
|
|
272
|
+
def test_consecutive_environmental_failures_still_stop_it(self):
|
|
273
|
+
provider = self._provider([("could_not_proceed: Unknown command", True)] * 9)
|
|
274
|
+
summary = _run(provider, [])
|
|
275
|
+
self.assertEqual(len(provider.executed), IDENTICAL_FAILURE_LIMIT)
|
|
276
|
+
self.assertEqual(summary.stop_reason, STOP_RUN_LEVEL_FAILURE)
|
|
277
|
+
|
|
278
|
+
def test_a_per_item_judgement_between_them_resets_the_count(self):
|
|
279
|
+
# A lane that successfully judges an item in between is not one whose
|
|
280
|
+
# environment is broken.
|
|
281
|
+
provider = self._provider([
|
|
282
|
+
("could_not_proceed: Unknown command", True),
|
|
283
|
+
("could_not_proceed: Unknown command", True),
|
|
284
|
+
("refused", False),
|
|
285
|
+
("could_not_proceed: Unknown command", True),
|
|
286
|
+
("could_not_proceed: Unknown command", True),
|
|
287
|
+
])
|
|
288
|
+
summary = _run(provider, [])
|
|
289
|
+
self.assertEqual(len(provider.executed), 5)
|
|
290
|
+
self.assertNotEqual(summary.stop_reason, STOP_RUN_LEVEL_FAILURE)
|
|
291
|
+
|
|
292
|
+
def test_environmental_defaults_false_so_a_provider_must_opt_in(self):
|
|
293
|
+
# Fail-safe direction: a provider that says nothing never trips the
|
|
294
|
+
# breaker, rather than tripping it by omission.
|
|
295
|
+
self.assertFalse(ActionOutcome(status=STATUS_ESCALATED, detail="x").environmental)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class TheProviderActuallySetsIt(unittest.TestCase):
|
|
299
|
+
"""Without this the breaker never fires in production and #3372's fix
|
|
300
|
+
would be one hollow guard replacing another."""
|
|
301
|
+
|
|
302
|
+
def _run_execute(self, *, outcome_named: bool, outcome: str):
|
|
303
|
+
from unittest.mock import patch
|
|
304
|
+
from specfuse.agent.providers.bugs import BugsProvider
|
|
305
|
+
from specfuse.loop.bug_lane_run import BugLaneResult
|
|
306
|
+
|
|
307
|
+
provider = BugsProvider(repo="acme/widget", runner=lambda *a, **k: None)
|
|
308
|
+
item = ActionItem(item_id="bug-7", kind="bug", summary="", queue_key=None)
|
|
309
|
+
result = BugLaneResult(
|
|
310
|
+
outcome=outcome, reason=None, pr_number=None,
|
|
311
|
+
stop_rationale="whatever", outcome_named=outcome_named,
|
|
312
|
+
)
|
|
313
|
+
with patch("specfuse.agent.providers.bugs.run_bug_lane", return_value=result), \
|
|
314
|
+
patch("specfuse.agent.providers.bugs._wip_ref_for_item", return_value=None):
|
|
315
|
+
return provider.execute(item)
|
|
316
|
+
|
|
317
|
+
def test_a_session_that_named_no_outcome_is_environmental(self):
|
|
318
|
+
self.assertTrue(self._run_execute(outcome_named=False, outcome="could_not_proceed").environmental)
|
|
319
|
+
|
|
320
|
+
def test_a_refusal_the_session_named_is_not(self):
|
|
321
|
+
self.assertFalse(self._run_execute(outcome_named=True, outcome="refused").environmental)
|
|
322
|
+
|
|
323
|
+
def test_a_named_could_not_proceed_is_not_either(self):
|
|
324
|
+
# The skill ran, read the issue, and could not proceed on ITS terms.
|
|
325
|
+
self.assertFalse(self._run_execute(outcome_named=True, outcome="could_not_proceed").environmental)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.22.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|