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.
Files changed (601) hide show
  1. {specfuse_loop-0.22.0/specfuse_loop.egg-info → specfuse_loop-0.22.1}/PKG-INFO +1 -1
  2. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/pyproject.toml +1 -1
  3. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/bugs.py +12 -1
  4. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/run.py +33 -5
  5. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/agent_policy.py +40 -0
  6. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/bug_lane_run.py +52 -2
  7. specfuse_loop-0.22.1/specfuse/loop/data/VERSION +1 -0
  8. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/loop.py +1 -1
  9. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1/specfuse_loop.egg-info}/PKG-INFO +1 -1
  10. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/SOURCES.txt +1 -0
  11. specfuse_loop-0.22.1/tests/test_required_checks.py +161 -0
  12. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_run_level_failure_breaker.py +113 -0
  13. specfuse_loop-0.22.0/specfuse/loop/data/VERSION +0 -1
  14. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/LICENSE +0 -0
  15. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/NOTICE +0 -0
  16. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/README.md +0 -0
  17. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/setup.cfg +0 -0
  18. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/__init__.py +0 -0
  19. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/budget.py +0 -0
  20. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/diagnose_invoke.py +0 -0
  21. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/drafting_answers.py +0 -0
  22. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/drafting_invoke.py +0 -0
  23. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/drafting_questions.py +0 -0
  24. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/driver_command.py +0 -0
  25. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/driver_invoke.py +0 -0
  26. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/invoke.py +0 -0
  27. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/monitoring_read.py +0 -0
  28. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/__init__.py +0 -0
  29. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/answers.py +0 -0
  30. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/feature.py +0 -0
  31. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/findings_autofix.py +0 -0
  32. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/findings_diagnose.py +0 -0
  33. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/providers/triage.py +0 -0
  34. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/queue_read.py +0 -0
  35. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/repo_detect.py +0 -0
  36. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/severity_backfill.py +0 -0
  37. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/state.py +0 -0
  38. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/triage_invoke.py +0 -0
  39. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/agent/worktree.py +0 -0
  40. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/__init__.py +0 -0
  41. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/_filelock.py +0 -0
  42. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/_miniyaml.py +0 -0
  43. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/_wu_sections.py +0 -0
  44. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/adopt_feature.py +0 -0
  45. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/arm_eval.py +0 -0
  46. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/arm_sweep.py +0 -0
  47. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/arm_txn.py +0 -0
  48. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/bug_lane.py +0 -0
  49. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/bug_lane_state.py +0 -0
  50. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/build_provenance.py +0 -0
  51. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/caller_check.py +0 -0
  52. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/changelog.py +0 -0
  53. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/closing_requirements.py +0 -0
  54. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/cost.py +0 -0
  55. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/criteria_state.py +0 -0
  56. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  57. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  58. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  59. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  60. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  61. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  62. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/getting-started.md +0 -0
  63. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/glossary.md +0 -0
  64. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/methodology.md +0 -0
  65. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/docs/skills.md +0 -0
  66. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/gitignore.snippet +0 -0
  67. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  68. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  69. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/monitoring.yml.example +0 -0
  70. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/roadmap.template.md +0 -0
  71. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/close-discipline.md +0 -0
  72. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  73. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  74. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/human-output.md +0 -0
  75. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/never-touch.md +0 -0
  76. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  77. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  78. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/result-contract.md +0 -0
  79. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  80. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  81. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/rules-local/README.md +0 -0
  82. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  83. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/event.schema.json +0 -0
  84. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  85. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  86. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  87. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  88. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  89. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/GATE.template.md +0 -0
  90. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  91. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  92. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/templates/WU.template.md +0 -0
  93. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/verification.yml.example +0 -0
  94. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  95. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/decisions_format.py +0 -0
  96. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/driver_edit.py +0 -0
  97. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/escalation.py +0 -0
  98. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/events_stats.py +0 -0
  99. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gate_commands.py +0 -0
  100. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gate_eval.py +0 -0
  101. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gh_backend.py +0 -0
  102. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/gh_features.py +0 -0
  103. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/heartbeat.py +0 -0
  104. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/judge.py +0 -0
  105. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/labels.py +0 -0
  106. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/learnings_distil.py +0 -0
  107. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/learnings_query.py +0 -0
  108. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_closing.py +0 -0
  109. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_monitoring.py +0 -0
  110. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_plan.py +0 -0
  111. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/lint_roadmap.py +0 -0
  112. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/notify.py +0 -0
  113. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/notify_escalation.py +0 -0
  114. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/notify_sla.py +0 -0
  115. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/plan_baseline.py +0 -0
  116. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/policy_proposals.py +0 -0
  117. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/policy_review.py +0 -0
  118. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/prerun.py +0 -0
  119. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/prerun_capture.py +0 -0
  120. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/promotion.py +0 -0
  121. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/rearm_migration.py +0 -0
  122. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/replay_spin.py +0 -0
  123. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/roadmap_archive.py +0 -0
  124. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/scaffold.py +0 -0
  125. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/triage.py +0 -0
  126. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/upgrade_merge_gate.py +0 -0
  127. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/loop/validate_event.py +0 -0
  128. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/__init__.py +0 -0
  129. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/adapters.py +0 -0
  130. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/artifact.py +0 -0
  131. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix.py +0 -0
  132. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix_invoke.py +0 -0
  133. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix_run.py +0 -0
  134. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/autofix_state.py +0 -0
  135. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/cli.py +0 -0
  136. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/diagnose_cli.py +0 -0
  137. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/diagnosis.py +0 -0
  138. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/fingerprint.py +0 -0
  139. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/issues.py +0 -0
  140. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/__init__.py +0 -0
  141. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/_azure_auth.py +0 -0
  142. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  143. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  144. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/redaction.py +0 -0
  145. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse/monitor/schedule.py +0 -0
  146. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  147. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/entry_points.txt +0 -0
  148. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/requires.txt +0 -0
  149. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/specfuse_loop.egg-info/top_level.txt +0 -0
  150. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_abandon_feature_skill.py +0 -0
  151. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_abandoned_work_reported.py +0 -0
  152. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_abandoned_wu_strands_gate.py +0 -0
  153. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_adopt_feature.py +0 -0
  154. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_budget.py +0 -0
  155. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_bug_selection_dedup.py +0 -0
  156. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_default_features_root.py +0 -0
  157. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_driver_command.py +0 -0
  158. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_driver_invoke.py +0 -0
  159. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_driver_restart.py +0 -0
  160. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_escalation_on_issue.py +0 -0
  161. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_escalation_run_state.py +0 -0
  162. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_findings_seam.py +0 -0
  163. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_invoke_timeout.py +0 -0
  164. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_invoke_usage.py +0 -0
  165. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_item_worktree.py +0 -0
  166. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_lock.py +0 -0
  167. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_key_ownership.py +0 -0
  168. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_queue.py +0 -0
  169. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_schema.py +0 -0
  170. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_policy_triage_dial.py +0 -0
  171. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_answers.py +0 -0
  172. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_bugs.py +0 -0
  173. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_feature.py +0 -0
  174. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_findings_autofix.py +0 -0
  175. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_findings_diagnose.py +0 -0
  176. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_isolation.py +0 -0
  177. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_provider_triage.py +0 -0
  178. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_queue_read.py +0 -0
  179. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_relays_driver_output.py +0 -0
  180. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_repo_detect.py +0 -0
  181. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_run.py +0 -0
  182. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_run_progress.py +0 -0
  183. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_seam.py +0 -0
  184. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_snapshot_refresh.py +0 -0
  185. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_agent_state.py +0 -0
  186. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_answer_escalation_skill.py +0 -0
  187. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_app_insights_adapters.py +0 -0
  188. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_archive_ref_reconciliation.py +0 -0
  189. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_eval.py +0 -0
  190. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_eval_lint_class.py +0 -0
  191. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_eval_wiring.py +0 -0
  192. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  193. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_sweep.py +0 -0
  194. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_sweep_gate.py +0 -0
  195. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_txn.py +0 -0
  196. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_arm_wiring.py +0 -0
  197. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_artifact_redaction.py +0 -0
  198. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attempt_note_untracked_files.py +0 -0
  199. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attempt_outcome_contract.py +0 -0
  200. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attempt_outcome_emission.py +0 -0
  201. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attention_nonwriting_guard.py +0 -0
  202. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attention_silence_section.py +0 -0
  203. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_attention_skill_structure.py +0 -0
  204. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autoclose_deferral_visibility.py +0 -0
  205. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autoclose_gate_note.py +0 -0
  206. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  207. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_invoke.py +0 -0
  208. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_live.py +0 -0
  209. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_predicate.py +0 -0
  210. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_run.py +0 -0
  211. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autofix_state.py +0 -0
  212. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync.py +0 -0
  213. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_consent.py +0 -0
  214. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_firstrun.py +0 -0
  215. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_no_cwd_leak.py +0 -0
  216. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_autosync_plugin.py +0 -0
  217. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_azure_credential_resolution.py +0 -0
  218. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_backend.py +0 -0
  219. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_escalation_message.py +0 -0
  220. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_persistence.py +0 -0
  221. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_probe.py +0 -0
  222. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_provenance.py +0 -0
  223. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_baseline_tree_hash_key.py +0 -0
  224. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bash_routing.py +0 -0
  225. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bats_suites_gated.py +0 -0
  226. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binary_verdict.py +0 -0
  227. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binding_block_advisory.py +0 -0
  228. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binding_block_allocation.py +0 -0
  229. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_binding_block_budget.py +0 -0
  230. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  231. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  232. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bookkeeping_no_verify.py +0 -0
  233. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  234. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_ci_pending.py +0 -0
  235. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_guardrails.py +0 -0
  236. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_label_nonfatal.py +0 -0
  237. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_labels_registered.py +0 -0
  238. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_min_severity.py +0 -0
  239. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_policy_contract.py +0 -0
  240. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_pr_number_carried.py +0 -0
  241. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  242. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_run.py +0 -0
  243. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_state.py +0 -0
  244. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  245. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_bug_lane_test_paths.py +0 -0
  246. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_build_provenance.py +0 -0
  247. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_caller_check_ratchet.py +0 -0
  248. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changed_file_test_selection.py +0 -0
  249. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_append_only.py +0 -0
  250. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_collection.py +0 -0
  251. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_release_wiring.py +0 -0
  252. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_changelog_schema.py +0 -0
  253. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ci_conclusion_wait.py +0 -0
  254. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ci_runs_every_gate.py +0 -0
  255. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_claude_resolution.py +0 -0
  256. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_close_b_pending_learnings.py +0 -0
  257. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_close_records_feature_oracle.py +0 -0
  258. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_close_wu_self_reference.py +0 -0
  259. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_deliverable_guard.py +0 -0
  260. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_guard_contracts.py +0 -0
  261. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_guard_prediction.py +0 -0
  262. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_learnings_destination.py +0 -0
  263. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_closing_requirements.py +0 -0
  264. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_coauthor_trailer_allowlist.py +0 -0
  265. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_conditional_reflection.py +0 -0
  266. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_console_encoding.py +0 -0
  267. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_convergent_iteration.py +0 -0
  268. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_convergent_wu_lint.py +0 -0
  269. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_correlation_id_override.py +0 -0
  270. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_correlation_id_two_surfaces.py +0 -0
  271. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_cost_lifetime.py +0 -0
  272. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_cost_tracking.py +0 -0
  273. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_criteria_per_gate_lint.py +0 -0
  274. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_criteria_state.py +0 -0
  275. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_criteria_worklist.py +0 -0
  276. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decision_citation_lint.py +0 -0
  277. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decision_followups_discharged.py +0 -0
  278. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decision_override_lint.py +0 -0
  279. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_decisions_format.py +0 -0
  280. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_default_severity_aliases.py +0 -0
  281. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_degraded_oracle.py +0 -0
  282. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_deliverable_missing_note.py +0 -0
  283. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_deliverable_presence_gate.py +0 -0
  284. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_agent_policy_review_mode.py +0 -0
  285. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_agent_policy_skill.py +0 -0
  286. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_monitoring_discovery.py +0 -0
  287. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_derive_monitoring_skill_registration.py +0 -0
  288. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_design_for_diagnosis_rule.py +0 -0
  289. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_deterministic_refusal_repeat.py +0 -0
  290. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnose_headless.py +0 -0
  291. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnose_issue_skill.py +0 -0
  292. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnosis.py +0 -0
  293. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_diagnosis_roundtrip_live.py +0 -0
  294. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_dispatch_skeleton.py +0 -0
  295. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_distilled_accept_step.py +0 -0
  296. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_doctor.py +0 -0
  297. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_doctor_command_preflight.py +0 -0
  298. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_done_feature_gates.py +0 -0
  299. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_draft_feature_answers_mode.py +0 -0
  300. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_answer_gate.py +0 -0
  301. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_invoke.py +0 -0
  302. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_question_issue.py +0 -0
  303. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_questions.py +0 -0
  304. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_drafting_reply_shape.py +0 -0
  305. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_edit_detection.py +0 -0
  306. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  307. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_integration.py +0 -0
  308. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_lock.py +0 -0
  309. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_module_surface.py +0 -0
  310. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_restart_halt_surfaced.py +0 -0
  311. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_restart_halt_wiring.py +0 -0
  312. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_restart_hold.py +0 -0
  313. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_staleness_gate_summary.py +0 -0
  314. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_driver_staleness_warning.py +0 -0
  315. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_dry_run_no_branch_mutation.py +0 -0
  316. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_duration_tracking.py +0 -0
  317. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_empty_files_escalation.py +0 -0
  318. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ensure_feature_branch.py +0 -0
  319. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ensure_feature_branch_base.py +0 -0
  320. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_assignee_policy.py +0 -0
  321. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_contract.py +0 -0
  322. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_crash_containment.py +0 -0
  323. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_dedup_search.py +0 -0
  324. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_escalation_emit.py +0 -0
  325. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_event_gate_full_envelope.py +0 -0
  326. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_events_redaction.py +0 -0
  327. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_events_stats.py +0 -0
  328. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_extra_gates.py +0 -0
  329. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_failure_artifact_model.py +0 -0
  330. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_failure_signature_fail_prefix.py +0 -0
  331. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_failure_signature_skips_log_noise.py +0 -0
  332. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_branch_auto_sync.py +0 -0
  333. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_branch_squash_merge.py +0 -0
  334. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_branch_stale_base.py +0 -0
  335. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_id_resolution.py +0 -0
  336. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_oracle_e2e.py +0 -0
  337. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  338. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_feature_review_accumulation.py +0 -0
  339. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_filelock_portable.py +0 -0
  340. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_files_changed_auto_repair.py +0 -0
  341. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_find_pr_no_search_index.py +0 -0
  342. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fingerprint.py +0 -0
  343. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fix_bug_diff_self_check.py +0 -0
  344. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fix_bug_headless.py +0 -0
  345. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fix_bug_reads_comments.py +0 -0
  346. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_fold_contract_documented.py +0 -0
  347. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_followups_artifact.py +0 -0
  348. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_force_full_close.py +0 -0
  349. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_frontmatter_scalar_quoting.py +0 -0
  350. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_broad_run.py +0 -0
  351. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_budget.py +0 -0
  352. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_budget_post_dispatch.py +0 -0
  353. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_commands_needs.py +0 -0
  354. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval.py +0 -0
  355. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_calibration.py +0 -0
  356. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  357. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_repo_flag.py +0 -0
  358. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_eval_terminal_wiring.py +0 -0
  359. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gate_report_verdict.py +0 -0
  360. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gh_backend.py +0 -0
  361. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gh_features.py +0 -0
  362. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_git_env_isolation.py +0 -0
  363. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_gitleaks_pinning.py +0 -0
  364. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_groom_backlog_skill.py +0 -0
  365. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_refusal_failure_class.py +0 -0
  366. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  367. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_repair_e2e.py +0 -0
  368. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_guard_repair_prompt.py +0 -0
  369. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_hashed_denylist.py +0 -0
  370. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_hashed_denylist_ci.py +0 -0
  371. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_heartbeat.py +0 -0
  372. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_heartbeat_adapter.py +0 -0
  373. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_human_output_rule.py +0 -0
  374. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_human_work_unit.py +0 -0
  375. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_hyphenated_gate_name.py +0 -0
  376. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_init_integration.py +0 -0
  377. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_installed_copy_driver_e2e.py +0 -0
  378. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_interpreter_normalization.py +0 -0
  379. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_close_path.py +0 -0
  380. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_cost.py +0 -0
  381. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_cost_fold_committed.py +0 -0
  382. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_module.py +0 -0
  383. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_path_registry.py +0 -0
  384. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_judge_terminal_flip_state.py +0 -0
  385. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_label_provisioning_runner_contract.py +0 -0
  386. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_label_registry.py +0 -0
  387. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_label_registry_covers_consumers.py +0 -0
  388. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_language_aware_narrow_selection.py +0 -0
  389. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lazy_baseline_e2e.py +0 -0
  390. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_leak_findings_redaction.py +0 -0
  391. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_leak_scan.py +0 -0
  392. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_leak_scan_content.py +0 -0
  393. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_curate_vendored_promote.py +0 -0
  394. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_distil_cli.py +0 -0
  395. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_query.py +0 -0
  396. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_staging.py +0 -0
  397. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_suggest_signature_filter.py +0 -0
  398. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_learnings_weights.py +0 -0
  399. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  400. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lifecycle_integration.py +0 -0
  401. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lifetime_accounting.py +0 -0
  402. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_ac_observable.py +0 -0
  403. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_bare_produces_path.py +0 -0
  404. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_boundary_consistency.py +0 -0
  405. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_boundary_extraction.py +0 -0
  406. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_close_intermediate.py +0 -0
  407. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  408. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_close_wu.py +0 -0
  409. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_closing.py +0 -0
  410. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_closing_criteria.py +0 -0
  411. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_closing_criteria_pristine.py +0 -0
  412. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_correlation_id.py +0 -0
  413. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  414. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  415. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_feature_oracle_declared.py +0 -0
  416. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_gate_proportionality.py +0 -0
  417. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_monitoring.py +0 -0
  418. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_oracle_env.py +0 -0
  419. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_contract_fields.py +0 -0
  420. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_errors.py +0 -0
  421. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_next_draft.py +0 -0
  422. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_relative_import.py +0 -0
  423. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_plan_verdict_exempt.py +0 -0
  424. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_planning_sections.py +0 -0
  425. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_produces_driver_helper.py +0 -0
  426. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_produces_satisfiability.py +0 -0
  427. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_roadmap.py +0 -0
  428. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_sections.py +0 -0
  429. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  430. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_lint_verification_reference.py +0 -0
  431. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_live_tests_opt_in.py +0 -0
  432. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_load_bearing_close_lint.py +0 -0
  433. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_attempt_evidence.py +0 -0
  434. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_auto_archive.py +0 -0
  435. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_caveman_preamble.py +0 -0
  436. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_close_intermediate.py +0 -0
  437. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_criteria_skeleton.py +0 -0
  438. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_criteria_survival.py +0 -0
  439. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_defaults_by_type.py +0 -0
  440. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_effort.py +0 -0
  441. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_failure_note_cap.py +0 -0
  442. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_files_changed_guard.py +0 -0
  443. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_gate_budget.py +0 -0
  444. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_gate_needs.py +0 -0
  445. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_model_alias.py +0 -0
  446. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_orchestration.py +0 -0
  447. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_post_pass_invariant.py +0 -0
  448. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_reset_preserving_events.py +0 -0
  449. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_smoke_runner.py +0 -0
  450. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_unsandboxed.py +0 -0
  451. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_worklist_injection.py +0 -0
  452. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_loop_zero_token_guard.py +0 -0
  453. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  454. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_marker_dual_shape.py +0 -0
  455. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_maven_failure_excerpt.py +0 -0
  456. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_max_attempts_configurable.py +0 -0
  457. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_max_open_prs.py +0 -0
  458. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_migrate_legacy.py +0 -0
  459. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_miniyaml_equivalence.py +0 -0
  460. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_miniyaml_negative.py +0 -0
  461. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitor_cli.py +0 -0
  462. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitor_issue_lifecycle.py +0 -0
  463. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitor_runner_surfaces.py +0 -0
  464. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  465. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_cron_dialect.py +0 -0
  466. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_example.py +0 -0
  467. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_fenced_blocks.py +0 -0
  468. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_monitoring_seed.py +0 -0
  469. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_narrow_fallback_warning.py +0 -0
  470. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_no_pytest_subprocess.py +0 -0
  471. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_notify.py +0 -0
  472. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_notify_escalation.py +0 -0
  473. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_notify_sla.py +0 -0
  474. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_operator_escalation_rule.py +0 -0
  475. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_oracle_set_declared.py +0 -0
  476. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_oracle_unrunnable_attribution.py +0 -0
  477. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pick_feature_skill.py +0 -0
  478. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pin_honesty_and_integrity.py +0 -0
  479. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pin_marker_not_inherited.py +0 -0
  480. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pin_marker_spawn_sites.py +0 -0
  481. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_plan_baseline.py +0 -0
  482. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_planned_cost_lint.py +0 -0
  483. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_planning_cost_floor.py +0 -0
  484. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_plugin_qualified_dispatch.py +0 -0
  485. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_policy_budgets_applied.py +0 -0
  486. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_policy_proposals.py +0 -0
  487. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_policy_review.py +0 -0
  488. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pr_checks_json_field.py +0 -0
  489. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pr_reference_in_title.py +0 -0
  490. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  491. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prep_halt_operator_surface.py +0 -0
  492. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prepare_feature.py +0 -0
  493. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prepare_scaffold_sync.py +0 -0
  494. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prerun_capture.py +0 -0
  495. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prerun_oracles.py +0 -0
  496. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_prerun_wiring.py +0 -0
  497. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produced_document_verdict_guard.py +0 -0
  498. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_bracket_literal.py +0 -0
  499. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_bracket_wildcard.py +0 -0
  500. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_field.py +0 -0
  501. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_in_diff.py +0 -0
  502. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_justification.py +0 -0
  503. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_recursive_glob.py +0 -0
  504. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_semantics_unified.py +0 -0
  505. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_produces_shape.py +0 -0
  506. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_progress_forward_note.py +0 -0
  507. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_progress_lines_end_to_end.py +0 -0
  508. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_promotion_ledger.py +0 -0
  509. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_provider_escalation_traces.py +0 -0
  510. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_provision_labels.py +0 -0
  511. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_queue_stalled_adapter.py +0 -0
  512. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  513. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rearm_fold_marker.py +0 -0
  514. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rearm_migration.py +0 -0
  515. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_red_baseline_never_reused.py +0 -0
  516. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_redaction_windows_home.py +0 -0
  517. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_reflection_required_signals.py +0 -0
  518. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_end_to_end.py +0 -0
  519. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_event_emission.py +0 -0
  520. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_note_collision.py +0 -0
  521. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_trigger.py +0 -0
  522. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replan_turn_contract.py +0 -0
  523. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_replay_spin.py +0 -0
  524. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_resolve_base.py +0 -0
  525. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_restart_cap_counts_progress.py +0 -0
  526. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_result_block.py +0 -0
  527. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_result_block_audience.py +0 -0
  528. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_resume_command_build.py +0 -0
  529. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  530. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_add_skill.py +0 -0
  531. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_cli.py +0 -0
  532. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_shim.py +0 -0
  533. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_skill.py +0 -0
  534. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  535. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_link_gate.py +0 -0
  536. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_roadmap_row_parser.py +0 -0
  537. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_ruff_verdict_lines.py +0 -0
  538. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_rule_import_convergence.py +0 -0
  539. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_data_in_sync.py +0 -0
  540. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_doc_hygiene.py +0 -0
  541. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_docs.py +0 -0
  542. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_init.py +0 -0
  543. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_label_provisioning.py +0 -0
  544. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_manifest.py +0 -0
  545. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_resources.py +0 -0
  546. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_seed_sanity.py +0 -0
  547. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_skew_halts.py +0 -0
  548. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_upgrade.py +0 -0
  549. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_scaffold_wiring.py +0 -0
  550. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_schedule_dialect.py +0 -0
  551. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_seed_documents_prerun_keys.py +0 -0
  552. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_service_bus_dlq_adapter.py +0 -0
  553. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_session_env_preflight.py +0 -0
  554. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_aliases.py +0 -0
  555. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_apply.py +0 -0
  556. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_end_to_end.py +0 -0
  557. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_limit.py +0 -0
  558. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_marker.py +0 -0
  559. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_backfill_run.py +0 -0
  560. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_severity_rubric.py +0 -0
  561. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_shipped_templates_parse.py +0 -0
  562. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_skill_discovery_links.py +0 -0
  563. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_skills_vendored_in_sync.py +0 -0
  564. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_spinning_rearm_gate.py +0 -0
  565. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_spinout_brief_end_to_end.py +0 -0
  566. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_spinout_brief_replan_option.py +0 -0
  567. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_squash_commit_hook_crash.py +0 -0
  568. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_stop_rationale_captured.py +0 -0
  569. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_stranded_halt_cause.py +0 -0
  570. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_surefire_run_level_signature.py +0 -0
  571. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_surefire_verdict_lines.py +0 -0
  572. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_template_closing_shapes.py +0 -0
  573. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_terminal_flip_ownership.py +0 -0
  574. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_terminal_flips.py +0 -0
  575. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_terminal_gate_message.py +0 -0
  576. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_tiered_verification_e2e.py +0 -0
  577. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_timeout_kill_portable.py +0 -0
  578. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage.py +0 -0
  579. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_apply.py +0 -0
  580. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_severity_classify.py +0 -0
  581. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_severity_end_to_end.py +0 -0
  582. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_severity_write.py +0 -0
  583. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_skill_contract.py +0 -0
  584. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_triage_skips_agent_escalations.py +0 -0
  585. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_tsc_verdict_lines.py +0 -0
  586. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_umbrella_version_resolution.py +0 -0
  587. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_unterminated_frontmatter.py +0 -0
  588. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_untracked_feature_folder.py +0 -0
  589. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_untracked_file_absorption.py +0 -0
  590. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_upgrade_integration.py +0 -0
  591. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_upgrade_merge_gate.py +0 -0
  592. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_validate_event.py +0 -0
  593. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_validate_event_driver_types.py +0 -0
  594. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_verdict_coupling.py +0 -0
  595. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_verify_empty_gate_set.py +0 -0
  596. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_version_consistency.py +0 -0
  597. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_version_skew.py +0 -0
  598. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_windows_gate_exec.py +0 -0
  599. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_worktree_restore.py +0 -0
  600. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_wu_execution_metadata.py +0 -0
  601. {specfuse_loop-0.22.0 → specfuse_loop-0.22.1}/tests/test_wu_template_renders_lintable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.22.0
3
+ Version: 0.22.1
4
4
  Summary: Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology.
5
5
  Author: Specfuse contributors
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specfuse-loop"
7
- version = "0.22.0"
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
- return ActionOutcome(status=STATUS_ESCALATED, detail=detail, escalation=escalation)
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
- signature = failure_signature(item, outcome)
1007
- if signature == last_failure_signature:
1008
- consecutive_identical_failures += 1
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
- last_failure_signature = signature
1011
- consecutive_identical_failures = 1
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 _read_ci_conclusion_once(runner: Callable, repo: str, pr_number: int) -> str:
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(runner, repo, pr_number)
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.0"
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.22.0
3
+ Version: 0.22.1
4
4
  Summary: Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology.
5
5
  Author: Specfuse contributors
6
6
  License: Apache-2.0
@@ -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