specfuse-loop 0.26.0__tar.gz → 0.27.0__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 (648) hide show
  1. {specfuse_loop-0.26.0/specfuse_loop.egg-info → specfuse_loop-0.27.0}/PKG-INFO +1 -1
  2. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/pyproject.toml +1 -1
  3. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/bugs.py +37 -13
  4. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/run.py +43 -21
  5. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/severity_backfill.py +4 -4
  6. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/state.py +3 -1
  7. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/bug_lane_run.py +180 -19
  8. specfuse_loop-0.27.0/specfuse/loop/data/VERSION +1 -0
  9. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/escalation.py +27 -8
  10. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/loop.py +102 -1
  11. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/red_on_base.py +94 -13
  12. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/triage.py +42 -5
  13. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix_invoke.py +11 -0
  14. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
  15. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/SOURCES.txt +15 -0
  16. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_escalation_on_issue.py +9 -2
  17. specfuse_loop-0.27.0/tests/test_bug_lane_no_checks_pending.py +99 -0
  18. specfuse_loop-0.27.0/tests/test_bug_lane_spend.py +160 -0
  19. specfuse_loop-0.27.0/tests/test_bug_lane_superseded.py +216 -0
  20. specfuse_loop-0.27.0/tests/test_fix_bug_cwd_worktree.py +81 -0
  21. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_broad_run.py +10 -0
  22. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_intermediate_wiring.py +4 -0
  23. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_terminal_wiring.py +13 -0
  24. specfuse_loop-0.27.0/tests/test_headless_no_background_tasks.py +57 -0
  25. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lifecycle_integration.py +10 -0
  26. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_max_open_prs.py +6 -5
  27. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_amendment_e2e.py +10 -0
  28. specfuse_loop-0.27.0/tests/test_promoted_issue_not_redispatched.py +102 -0
  29. specfuse_loop-0.27.0/tests/test_red_on_base_fetch_and_reason.py +173 -0
  30. specfuse_loop-0.27.0/tests/test_red_on_base_overlay.py +164 -0
  31. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_red_on_base_probe.py +22 -18
  32. specfuse_loop-0.27.0/tests/test_red_on_base_real_runner.py +105 -0
  33. specfuse_loop-0.27.0/tests/test_reescalation_posts_comment.py +104 -0
  34. specfuse_loop-0.27.0/tests/test_result_block_near_miss.py +54 -0
  35. specfuse_loop-0.27.0/tests/test_result_block_scalar.py +72 -0
  36. specfuse_loop-0.27.0/tests/test_severity_backfill_bugs_only.py +57 -0
  37. specfuse_loop-0.27.0/tests/test_terminal_auto_close_changelog.py +97 -0
  38. specfuse_loop-0.27.0/tests/test_unreadable_pr_listing_holds_pr_work.py +91 -0
  39. specfuse_loop-0.26.0/specfuse/loop/data/VERSION +0 -1
  40. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/LICENSE +0 -0
  41. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/NOTICE +0 -0
  42. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/README.md +0 -0
  43. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/setup.cfg +0 -0
  44. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/__init__.py +0 -0
  45. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/budget.py +0 -0
  46. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/diagnose_invoke.py +0 -0
  47. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/drafting_answers.py +0 -0
  48. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/drafting_invoke.py +0 -0
  49. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/drafting_questions.py +0 -0
  50. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/driver_command.py +0 -0
  51. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/driver_invoke.py +0 -0
  52. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/invoke.py +0 -0
  53. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/monitoring_read.py +0 -0
  54. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/__init__.py +0 -0
  55. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/answers.py +0 -0
  56. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/feature.py +0 -0
  57. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/findings_autofix.py +0 -0
  58. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/findings_diagnose.py +0 -0
  59. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/providers/triage.py +0 -0
  60. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/queue_read.py +0 -0
  61. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/repo_detect.py +0 -0
  62. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/triage_invoke.py +0 -0
  63. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/agent/worktree.py +0 -0
  64. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/__init__.py +0 -0
  65. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/_filelock.py +0 -0
  66. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/_miniyaml.py +0 -0
  67. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/_wu_sections.py +0 -0
  68. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/adopt_feature.py +0 -0
  69. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/agent_policy.py +0 -0
  70. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/arm_eval.py +0 -0
  71. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/arm_sweep.py +0 -0
  72. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/arm_txn.py +0 -0
  73. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/bug_lane.py +0 -0
  74. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/bug_lane_state.py +0 -0
  75. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/build_provenance.py +0 -0
  76. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/caller_check.py +0 -0
  77. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/changelog.py +0 -0
  78. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/closing_requirements.py +0 -0
  79. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/cost.py +0 -0
  80. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/criteria_state.py +0 -0
  81. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  82. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  83. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  84. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  85. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  86. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  87. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/getting-started.md +0 -0
  88. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/glossary.md +0 -0
  89. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/methodology.md +0 -0
  90. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/docs/skills.md +0 -0
  91. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/gitignore.snippet +0 -0
  92. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  93. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  94. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/monitoring.yml.example +0 -0
  95. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/roadmap.template.md +0 -0
  96. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/close-discipline.md +0 -0
  97. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  98. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  99. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/human-output.md +0 -0
  100. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/never-touch.md +0 -0
  101. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  102. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  103. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/result-contract.md +0 -0
  104. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  105. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  106. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/rules-local/README.md +0 -0
  107. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  108. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/event.schema.json +0 -0
  109. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  110. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  111. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  112. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  113. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  114. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/GATE.template.md +0 -0
  115. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  116. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  117. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/templates/WU.template.md +0 -0
  118. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/verification.yml.example +0 -0
  119. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  120. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/decisions_format.py +0 -0
  121. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/driver_edit.py +0 -0
  122. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/events_stats.py +0 -0
  123. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gate_commands.py +0 -0
  124. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gate_eval.py +0 -0
  125. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gh_backend.py +0 -0
  126. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/gh_features.py +0 -0
  127. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/heartbeat.py +0 -0
  128. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/judge.py +0 -0
  129. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/labels.py +0 -0
  130. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/learnings_distil.py +0 -0
  131. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/learnings_query.py +0 -0
  132. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_closing.py +0 -0
  133. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_monitoring.py +0 -0
  134. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_plan.py +0 -0
  135. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/lint_roadmap.py +0 -0
  136. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/notify.py +0 -0
  137. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/notify_escalation.py +0 -0
  138. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/notify_sla.py +0 -0
  139. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/plan_baseline.py +0 -0
  140. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/policy_proposals.py +0 -0
  141. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/policy_review.py +0 -0
  142. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/prerun.py +0 -0
  143. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/prerun_capture.py +0 -0
  144. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/promotion.py +0 -0
  145. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/rearm_migration.py +0 -0
  146. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/replay_spin.py +0 -0
  147. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/roadmap_archive.py +0 -0
  148. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/scaffold.py +0 -0
  149. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/upgrade_merge_gate.py +0 -0
  150. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/loop/validate_event.py +0 -0
  151. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/__init__.py +0 -0
  152. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/adapters.py +0 -0
  153. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/artifact.py +0 -0
  154. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix.py +0 -0
  155. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix_run.py +0 -0
  156. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/autofix_state.py +0 -0
  157. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/cli.py +0 -0
  158. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/diagnose_cli.py +0 -0
  159. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/diagnosis.py +0 -0
  160. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/fingerprint.py +0 -0
  161. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/issues.py +0 -0
  162. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/__init__.py +0 -0
  163. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
  164. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  165. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  166. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/redaction.py +0 -0
  167. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse/monitor/schedule.py +0 -0
  168. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  169. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
  170. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/requires.txt +0 -0
  171. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/specfuse_loop.egg-info/top_level.txt +0 -0
  172. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_abandon_feature_skill.py +0 -0
  173. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_abandoned_work_reported.py +0 -0
  174. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
  175. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_adopt_feature.py +0 -0
  176. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_budget.py +0 -0
  177. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_bug_selection_dedup.py +0 -0
  178. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_default_features_root.py +0 -0
  179. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_driver_command.py +0 -0
  180. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_driver_invoke.py +0 -0
  181. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_driver_restart.py +0 -0
  182. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_escalation_run_state.py +0 -0
  183. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_findings_seam.py +0 -0
  184. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_invoke_timeout.py +0 -0
  185. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_invoke_usage.py +0 -0
  186. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_item_worktree.py +0 -0
  187. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_lock.py +0 -0
  188. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_key_ownership.py +0 -0
  189. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_queue.py +0 -0
  190. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_schema.py +0 -0
  191. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_policy_triage_dial.py +0 -0
  192. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_answers.py +0 -0
  193. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_bugs.py +0 -0
  194. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_feature.py +0 -0
  195. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_findings_autofix.py +0 -0
  196. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
  197. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_isolation.py +0 -0
  198. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_provider_triage.py +0 -0
  199. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_queue_read.py +0 -0
  200. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_relays_driver_output.py +0 -0
  201. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_repo_detect.py +0 -0
  202. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_run.py +0 -0
  203. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_run_progress.py +0 -0
  204. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_seam.py +0 -0
  205. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_snapshot_refresh.py +0 -0
  206. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_agent_state.py +0 -0
  207. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_answer_escalation_skill.py +0 -0
  208. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_app_insights_adapters.py +0 -0
  209. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_archive_ref_reconciliation.py +0 -0
  210. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_eval.py +0 -0
  211. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_eval_lint_class.py +0 -0
  212. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_eval_wiring.py +0 -0
  213. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  214. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_sweep.py +0 -0
  215. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_sweep_gate.py +0 -0
  216. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_txn.py +0 -0
  217. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_arm_wiring.py +0 -0
  218. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_artifact_redaction.py +0 -0
  219. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attempt_note_untracked_files.py +0 -0
  220. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attempt_outcome_contract.py +0 -0
  221. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attempt_outcome_emission.py +0 -0
  222. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attention_nonwriting_guard.py +0 -0
  223. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attention_silence_section.py +0 -0
  224. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attention_skill_structure.py +0 -0
  225. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_attribution_halt_claims.py +0 -0
  226. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autoclose_deferral_visibility.py +0 -0
  227. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autoclose_gate_note.py +0 -0
  228. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  229. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_invoke.py +0 -0
  230. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_live.py +0 -0
  231. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_predicate.py +0 -0
  232. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_run.py +0 -0
  233. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autofix_state.py +0 -0
  234. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync.py +0 -0
  235. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_consent.py +0 -0
  236. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_firstrun.py +0 -0
  237. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_no_cwd_leak.py +0 -0
  238. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_autosync_plugin.py +0 -0
  239. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_azure_credential_resolution.py +0 -0
  240. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_backend.py +0 -0
  241. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_escalation_message.py +0 -0
  242. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_persistence.py +0 -0
  243. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_probe.py +0 -0
  244. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_provenance.py +0 -0
  245. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_baseline_tree_hash_key.py +0 -0
  246. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bash_routing.py +0 -0
  247. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bats_suites_gated.py +0 -0
  248. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binary_verdict.py +0 -0
  249. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binding_block_advisory.py +0 -0
  250. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binding_block_allocation.py +0 -0
  251. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_binding_block_budget.py +0 -0
  252. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_blind_run_is_not_drained.py +0 -0
  253. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  254. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  255. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bookkeeping_no_verify.py +0 -0
  256. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  257. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_ci_pending.py +0 -0
  258. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_guardrails.py +0 -0
  259. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
  260. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_labels_registered.py +0 -0
  261. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_min_severity.py +0 -0
  262. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_policy_contract.py +0 -0
  263. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
  264. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  265. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_run.py +0 -0
  266. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_state.py +0 -0
  267. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  268. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_bug_lane_test_paths.py +0 -0
  269. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_build_provenance.py +0 -0
  270. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_caller_check_ratchet.py +0 -0
  271. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_carried_green_invalidated_by_diff.py +0 -0
  272. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_carry_accounting_close_and_judge.py +0 -0
  273. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changed_file_test_selection.py +0 -0
  274. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_append_only.py +0 -0
  275. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_collection.py +0 -0
  276. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_release_wiring.py +0 -0
  277. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_changelog_schema.py +0 -0
  278. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ci_conclusion_wait.py +0 -0
  279. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ci_runs_every_gate.py +0 -0
  280. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_claude_resolution.py +0 -0
  281. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_close_b_pending_learnings.py +0 -0
  282. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_close_records_feature_oracle.py +0 -0
  283. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_close_wu_self_reference.py +0 -0
  284. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_deliverable_guard.py +0 -0
  285. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_guard_contracts.py +0 -0
  286. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_guard_prediction.py +0 -0
  287. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_learnings_destination.py +0 -0
  288. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_requirements.py +0 -0
  289. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_closing_sessions_on_sonnet.py +0 -0
  290. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
  291. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_conditional_reflection.py +0 -0
  292. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_console_encoding.py +0 -0
  293. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_convergent_iteration.py +0 -0
  294. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_convergent_wu_lint.py +0 -0
  295. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_correlation_id_override.py +0 -0
  296. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_correlation_id_two_surfaces.py +0 -0
  297. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_cost_lifetime.py +0 -0
  298. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_cost_tracking.py +0 -0
  299. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_criteria_per_gate_lint.py +0 -0
  300. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_criteria_state.py +0 -0
  301. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_criteria_worklist.py +0 -0
  302. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decision_citation_lint.py +0 -0
  303. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decision_followups_discharged.py +0 -0
  304. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decision_override_lint.py +0 -0
  305. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_decisions_format.py +0 -0
  306. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_default_severity_aliases.py +0 -0
  307. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_degraded_oracle.py +0 -0
  308. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_deliverable_missing_note.py +0 -0
  309. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_deliverable_presence_gate.py +0 -0
  310. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
  311. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_agent_policy_skill.py +0 -0
  312. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_monitoring_discovery.py +0 -0
  313. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
  314. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_design_for_diagnosis_rule.py +0 -0
  315. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_deterministic_refusal_repeat.py +0 -0
  316. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnose_headless.py +0 -0
  317. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnose_issue_skill.py +0 -0
  318. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnosis.py +0 -0
  319. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
  320. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_model_from_fix_scope.py +0 -0
  321. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
  322. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_skeleton.py +0 -0
  323. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dispatch_skills_index.py +0 -0
  324. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_distilled_accept_step.py +0 -0
  325. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_doctor.py +0 -0
  326. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_doctor_command_preflight.py +0 -0
  327. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_done_feature_gates.py +0 -0
  328. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_draft_feature_answers_mode.py +0 -0
  329. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_answer_gate.py +0 -0
  330. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_invoke.py +0 -0
  331. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_question_issue.py +0 -0
  332. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_questions.py +0 -0
  333. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_drafting_reply_shape.py +0 -0
  334. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_edit_detection.py +0 -0
  335. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  336. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_integration.py +0 -0
  337. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_lock.py +0 -0
  338. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_module_surface.py +0 -0
  339. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
  340. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_restart_halt_wiring.py +0 -0
  341. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_restart_hold.py +0 -0
  342. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_staleness_gate_summary.py +0 -0
  343. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_driver_staleness_warning.py +0 -0
  344. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
  345. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_duration_tracking.py +0 -0
  346. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_empty_files_escalation.py +0 -0
  347. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ensure_feature_branch.py +0 -0
  348. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ensure_feature_branch_base.py +0 -0
  349. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_assignee_policy.py +0 -0
  350. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_contract.py +0 -0
  351. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_crash_containment.py +0 -0
  352. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_dedup_search.py +0 -0
  353. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_escalation_emit.py +0 -0
  354. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_event_gate_full_envelope.py +0 -0
  355. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_events_redaction.py +0 -0
  356. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_events_stats.py +0 -0
  357. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_extra_gates.py +0 -0
  358. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_artifact_model.py +0 -0
  359. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_signature_fail_prefix.py +0 -0
  360. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_signature_names_the_test.py +0 -0
  361. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
  362. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_branch_auto_sync.py +0 -0
  363. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_branch_squash_merge.py +0 -0
  364. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_branch_stale_base.py +0 -0
  365. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_id_resolution.py +0 -0
  366. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_oracle_e2e.py +0 -0
  367. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  368. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_feature_review_accumulation.py +0 -0
  369. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_filelock_portable.py +0 -0
  370. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_files_changed_auto_repair.py +0 -0
  371. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_find_pr_no_search_index.py +0 -0
  372. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fingerprint.py +0 -0
  373. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_bug_diff_self_check.py +0 -0
  374. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_bug_headless.py +0 -0
  375. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_bug_reads_comments.py +0 -0
  376. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
  377. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_unit_insertion_e2e.py +0 -0
  378. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fix_unit_insertion_refused.py +0 -0
  379. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_fold_contract_documented.py +0 -0
  380. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_followups_artifact.py +0 -0
  381. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_force_full_close.py +0 -0
  382. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
  383. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_budget.py +0 -0
  384. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_budget_post_dispatch.py +0 -0
  385. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_commands_needs.py +0 -0
  386. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval.py +0 -0
  387. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_calibration.py +0 -0
  388. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_eval_repo_flag.py +0 -0
  389. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gate_report_verdict.py +0 -0
  390. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gh_backend.py +0 -0
  391. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gh_features.py +0 -0
  392. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_git_env_isolation.py +0 -0
  393. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_gitleaks_pinning.py +0 -0
  394. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_groom_backlog_skill.py +0 -0
  395. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_refusal_failure_class.py +0 -0
  396. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  397. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_repair_e2e.py +0 -0
  398. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_guard_repair_prompt.py +0 -0
  399. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hashed_denylist.py +0 -0
  400. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hashed_denylist_ci.py +0 -0
  401. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_heartbeat.py +0 -0
  402. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_heartbeat_adapter.py +0 -0
  403. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_human_output_rule.py +0 -0
  404. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_human_work_unit.py +0 -0
  405. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hygiene_boundary_guidance.py +0 -0
  406. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_hyphenated_gate_name.py +0 -0
  407. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ignored_cleanup_before_probe.py +0 -0
  408. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_init_integration.py +0 -0
  409. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_installed_copy_driver_e2e.py +0 -0
  410. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_interpreter_normalization.py +0 -0
  411. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_close_path.py +0 -0
  412. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_cost.py +0 -0
  413. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_cost_fold_committed.py +0 -0
  414. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_module.py +0 -0
  415. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_path_registry.py +0 -0
  416. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_sees_dropped_produces.py +0 -0
  417. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judge_terminal_flip_state.py +0 -0
  418. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_judged_event_points_at_findings.py +0 -0
  419. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_description_length.py +0 -0
  420. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_provisioning_runner_contract.py +0 -0
  421. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_registry.py +0 -0
  422. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_label_registry_covers_consumers.py +0 -0
  423. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_language_aware_narrow_selection.py +0 -0
  424. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lazy_baseline_e2e.py +0 -0
  425. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_leak_findings_redaction.py +0 -0
  426. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_leak_scan.py +0 -0
  427. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_leak_scan_content.py +0 -0
  428. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
  429. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_distil_cli.py +0 -0
  430. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_query.py +0 -0
  431. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_staging.py +0 -0
  432. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
  433. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_learnings_weights.py +0 -0
  434. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  435. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lifetime_accounting.py +0 -0
  436. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_ac_observable.py +0 -0
  437. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_bare_produces_path.py +0 -0
  438. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_boundary_consistency.py +0 -0
  439. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_boundary_extraction.py +0 -0
  440. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_close_intermediate.py +0 -0
  441. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  442. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_close_wu.py +0 -0
  443. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_closing.py +0 -0
  444. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_closing_criteria.py +0 -0
  445. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
  446. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_correlation_id.py +0 -0
  447. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  448. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  449. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_feature_oracle_declared.py +0 -0
  450. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_gate_proportionality.py +0 -0
  451. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_monitoring.py +0 -0
  452. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_oracle_env.py +0 -0
  453. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_contract_fields.py +0 -0
  454. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_errors.py +0 -0
  455. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_next_draft.py +0 -0
  456. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_relative_import.py +0 -0
  457. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
  458. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_planning_sections.py +0 -0
  459. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_produces_driver_helper.py +0 -0
  460. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_produces_satisfiability.py +0 -0
  461. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_roadmap.py +0 -0
  462. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_sections.py +0 -0
  463. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  464. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_lint_verification_reference.py +0 -0
  465. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_live_tests_opt_in.py +0 -0
  466. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_load_bearing_close_lint.py +0 -0
  467. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_attempt_evidence.py +0 -0
  468. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_auto_archive.py +0 -0
  469. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_caveman_preamble.py +0 -0
  470. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_close_intermediate.py +0 -0
  471. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_criteria_skeleton.py +0 -0
  472. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_criteria_survival.py +0 -0
  473. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_defaults_by_type.py +0 -0
  474. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_effort.py +0 -0
  475. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_failure_note_cap.py +0 -0
  476. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_files_changed_guard.py +0 -0
  477. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_gate_budget.py +0 -0
  478. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_gate_needs.py +0 -0
  479. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_model_alias.py +0 -0
  480. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_orchestration.py +0 -0
  481. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_post_pass_invariant.py +0 -0
  482. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_reset_preserving_events.py +0 -0
  483. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_smoke_runner.py +0 -0
  484. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_unsandboxed.py +0 -0
  485. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_worklist_injection.py +0 -0
  486. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_loop_zero_token_guard.py +0 -0
  487. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  488. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_marker_dual_shape.py +0 -0
  489. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_maven_failure_excerpt.py +0 -0
  490. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_max_attempts_configurable.py +0 -0
  491. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_migrate_legacy.py +0 -0
  492. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_miniyaml_equivalence.py +0 -0
  493. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_miniyaml_negative.py +0 -0
  494. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitor_cli.py +0 -0
  495. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitor_issue_lifecycle.py +0 -0
  496. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitor_runner_surfaces.py +0 -0
  497. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  498. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_cron_dialect.py +0 -0
  499. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_example.py +0 -0
  500. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_fenced_blocks.py +0 -0
  501. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_monitoring_seed.py +0 -0
  502. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_narrow_fallback_warning.py +0 -0
  503. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_needs_decision_outcome.py +0 -0
  504. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_no_pytest_subprocess.py +0 -0
  505. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_notify.py +0 -0
  506. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_notify_escalation.py +0 -0
  507. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_notify_sla.py +0 -0
  508. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_operator_escalation_rule.py +0 -0
  509. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_oracle_set_declared.py +0 -0
  510. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_oracle_unrunnable_attribution.py +0 -0
  511. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_per_run_item_cap_disclosure.py +0 -0
  512. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pick_feature_skill.py +0 -0
  513. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pin_honesty_and_integrity.py +0 -0
  514. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pin_marker_not_inherited.py +0 -0
  515. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pin_marker_spawn_sites.py +0 -0
  516. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_plan_baseline.py +0 -0
  517. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_planned_cost_lint.py +0 -0
  518. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_planning_cost_floor.py +0 -0
  519. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_plugin_prose_cross_refs.py +0 -0
  520. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_plugin_qualified_dispatch.py +0 -0
  521. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_policy_budgets_applied.py +0 -0
  522. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_policy_proposals.py +0 -0
  523. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_policy_review.py +0 -0
  524. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pr_checks_json_field.py +0 -0
  525. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pr_reference_in_title.py +0 -0
  526. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  527. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prep_halt_operator_surface.py +0 -0
  528. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prepare_feature.py +0 -0
  529. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prepare_scaffold_sync.py +0 -0
  530. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prerun_capture.py +0 -0
  531. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prerun_oracles.py +0 -0
  532. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_prerun_wiring.py +0 -0
  533. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produced_document_verdict_guard.py +0 -0
  534. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_bracket_literal.py +0 -0
  535. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_bracket_wildcard.py +0 -0
  536. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_field.py +0 -0
  537. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_in_diff.py +0 -0
  538. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_justification.py +0 -0
  539. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_recursive_glob.py +0 -0
  540. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_repair_note.py +0 -0
  541. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_semantics_unified.py +0 -0
  542. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_produces_shape.py +0 -0
  543. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_progress_forward_note.py +0 -0
  544. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_progress_lines_end_to_end.py +0 -0
  545. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_promotion_ledger.py +0 -0
  546. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_provider_escalation_traces.py +0 -0
  547. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_provision_labels.py +0 -0
  548. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_queue_stalled_adapter.py +0 -0
  549. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  550. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rearm_fold_marker.py +0 -0
  551. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rearm_migration.py +0 -0
  552. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
  553. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_red_baseline_never_reused.py +0 -0
  554. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_red_on_base_guardrail.py +0 -0
  555. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_redaction_windows_home.py +0 -0
  556. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_reflection_required_signals.py +0 -0
  557. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_end_to_end.py +0 -0
  558. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_event_emission.py +0 -0
  559. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_note_collision.py +0 -0
  560. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_trigger.py +0 -0
  561. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replan_turn_contract.py +0 -0
  562. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replay_spin.py +0 -0
  563. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_replay_spin_failing_sets.py +0 -0
  564. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_required_checks.py +0 -0
  565. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_resolve_base.py +0 -0
  566. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_restart_cap_counts_progress.py +0 -0
  567. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_result_block.py +0 -0
  568. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_result_block_audience.py +0 -0
  569. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_resume_command_build.py +0 -0
  570. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  571. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_add_skill.py +0 -0
  572. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_cli.py +0 -0
  573. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_shim.py +0 -0
  574. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_skill.py +0 -0
  575. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  576. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_link_gate.py +0 -0
  577. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_roadmap_row_parser.py +0 -0
  578. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_ruff_verdict_lines.py +0 -0
  579. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_rule_import_convergence.py +0 -0
  580. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_run_level_failure_breaker.py +0 -0
  581. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_data_in_sync.py +0 -0
  582. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_doc_hygiene.py +0 -0
  583. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_docs.py +0 -0
  584. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_init.py +0 -0
  585. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_label_provisioning.py +0 -0
  586. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_manifest.py +0 -0
  587. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_resources.py +0 -0
  588. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_seed_sanity.py +0 -0
  589. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_skew_halts.py +0 -0
  590. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_upgrade.py +0 -0
  591. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_scaffold_wiring.py +0 -0
  592. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_schedule_dialect.py +0 -0
  593. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_seed_documents_prerun_keys.py +0 -0
  594. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_service_bus_dlq_adapter.py +0 -0
  595. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_session_env_preflight.py +0 -0
  596. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_aliases.py +0 -0
  597. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_apply.py +0 -0
  598. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_end_to_end.py +0 -0
  599. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_limit.py +0 -0
  600. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_marker.py +0 -0
  601. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_backfill_run.py +0 -0
  602. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_label_projection.py +0 -0
  603. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_reconciliation.py +0 -0
  604. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_severity_rubric.py +0 -0
  605. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_shipped_templates_parse.py +0 -0
  606. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_skill_discovery_links.py +0 -0
  607. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_skills_vendored_in_sync.py +0 -0
  608. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spec_work_unit_rules.py +0 -0
  609. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinning_rearm_gate.py +0 -0
  610. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinning_repeat_same_failing_set.py +0 -0
  611. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinout_brief_end_to_end.py +0 -0
  612. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_spinout_brief_replan_option.py +0 -0
  613. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_squash_commit_hook_crash.py +0 -0
  614. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_stop_rationale_captured.py +0 -0
  615. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_stranded_halt_cause.py +0 -0
  616. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_surefire_run_level_signature.py +0 -0
  617. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_surefire_verdict_lines.py +0 -0
  618. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_template_closing_shapes.py +0 -0
  619. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_terminal_flip_ownership.py +0 -0
  620. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_terminal_flips.py +0 -0
  621. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_terminal_gate_message.py +0 -0
  622. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_tiered_verification_e2e.py +0 -0
  623. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_timeout_kill_portable.py +0 -0
  624. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage.py +0 -0
  625. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_apply.py +0 -0
  626. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_projects_severity_label.py +0 -0
  627. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_severity_classify.py +0 -0
  628. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_severity_end_to_end.py +0 -0
  629. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_severity_write.py +0 -0
  630. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_skill_contract.py +0 -0
  631. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_triage_skips_agent_escalations.py +0 -0
  632. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_tsc_verdict_lines.py +0 -0
  633. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_umbrella_version_resolution.py +0 -0
  634. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_unterminated_frontmatter.py +0 -0
  635. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_untracked_feature_folder.py +0 -0
  636. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_untracked_file_absorption.py +0 -0
  637. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_upgrade_integration.py +0 -0
  638. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_upgrade_merge_gate.py +0 -0
  639. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_validate_event.py +0 -0
  640. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_validate_event_driver_types.py +0 -0
  641. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_verdict_coupling.py +0 -0
  642. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_verify_empty_gate_set.py +0 -0
  643. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_version_consistency.py +0 -0
  644. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_version_skew.py +0 -0
  645. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_windows_gate_exec.py +0 -0
  646. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_worktree_restore.py +0 -0
  647. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/tests/test_wu_execution_metadata.py +0 -0
  648. {specfuse_loop-0.26.0 → specfuse_loop-0.27.0}/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.26.0
3
+ Version: 0.27.0
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.26.0"
7
+ version = "0.27.0"
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"
@@ -16,7 +16,7 @@ import subprocess
16
16
  import time
17
17
  from typing import Any, Callable, Optional, Sequence
18
18
 
19
- from specfuse.agent.invoke import resolve_item_timeout_seconds
19
+ from specfuse.agent.invoke import resolve_item_timeout_seconds, usage_spend
20
20
  from specfuse.agent.run import (
21
21
  KIND_BUG,
22
22
  STATUS_COMPLETED,
@@ -41,6 +41,7 @@ from specfuse.loop.bug_lane_run import (
41
41
  OUTCOME_DECLINED,
42
42
  OUTCOME_MERGED,
43
43
  OUTCOME_REFUSED,
44
+ OUTCOME_SUPERSEDED,
44
45
  REASON_PR_NOT_FOUND,
45
46
  pr_closes_issue,
46
47
  run_bug_lane,
@@ -63,7 +64,9 @@ _FIX_BUG_STOPPED_OUTCOMES = (OUTCOME_REFUSED, OUTCOME_COULD_NOT_PROCEED)
63
64
  #: "fix" its own "PR was declined by the merge guardrails" report.
64
65
  _HUMAN_OWNED_LABELS = frozenset({NEEDS_HUMAN_LABEL, "blocked-wu"})
65
66
 
66
- def _bounded_runner(runner: Callable, timeout_seconds: float) -> Callable:
67
+ def _bounded_runner(
68
+ runner: Callable, timeout_seconds: float, working_dir: str = "."
69
+ ) -> Callable:
67
70
  """Wrap *runner* so the headless `claude` dispatch inside `run_bug_lane`
68
71
  (`specfuse.loop.bug_lane_run:595`, off-limits to this WU) gets a real
69
72
  wall-clock timeout without editing that module.
@@ -72,12 +75,24 @@ def _bounded_runner(runner: Callable, timeout_seconds: float) -> Callable:
72
75
  `claude` -- `run_bug_lane` reuses this same runner for every `gh` read
73
76
  and write along the way, and those must not inherit an item-scale
74
77
  deadline meant for one long-running session.
78
+
79
+ The same call is started with `cwd=working_dir` (#3420). The item's
80
+ worktree used to reach the session only as a `Working directory:` line in
81
+ its prompt, so a session that followed `/fix-bug`'s own branch workflow
82
+ from where it was launched branched and committed in the operator's
83
+ primary checkout. The process boundary is what isolates it, not the text.
84
+
85
+ Any other keyword a caller passes is forwarded untouched (#3452). The
86
+ red-on-base probe runs its command with `cwd=<base worktree>`; a wrapper
87
+ that accepted only `check` raised `TypeError`, which the probe's
88
+ fail-closed `except` turned into `red_on_base_unverified` on every PR.
75
89
  """
76
90
 
77
- def wrapped(argv, check: bool = False):
78
- kwargs = {"check": check}
91
+ def wrapped(argv, check: bool = False, **kwargs):
92
+ kwargs["check"] = check
79
93
  if argv and argv[0] == "claude":
80
94
  kwargs["timeout"] = timeout_seconds
95
+ kwargs.setdefault("cwd", working_dir)
81
96
  return runner(argv, **kwargs)
82
97
 
83
98
  return wrapped
@@ -725,7 +740,7 @@ class BugsProvider:
725
740
  started = time.monotonic()
726
741
  try:
727
742
  result = run_bug_lane(
728
- _bounded_runner(self._runner, timeout_seconds),
743
+ _bounded_runner(self._runner, timeout_seconds, self._working_dir),
729
744
  self._repo,
730
745
  issue_number,
731
746
  working_dir=self._working_dir,
@@ -738,9 +753,11 @@ class BugsProvider:
738
753
  status=STATUS_ESCALATED,
739
754
  detail=(
740
755
  f"could_not_proceed: headless /fix-bug session timed out "
741
- f"after {elapsed:.0f}s"
756
+ f"after {elapsed:.0f}s (its token spend is not counted)"
742
757
  ),
743
758
  escalation=_timed_out_payload(issue_number, elapsed),
759
+ # A session cut off by the timeout printed no usage envelope,
760
+ # so its spend is unknown, not zero (#3419) -- said in `detail`.
744
761
  spend=0,
745
762
  )
746
763
 
@@ -750,17 +767,23 @@ class BugsProvider:
750
767
  # and therefore unevaluable — the one point the proposal called out as
751
768
  # mattering most.
752
769
  profile_note = _dispatch_profile_note(result.dispatch_profile)
770
+ # #3419: what the headless `/fix-bug` session spent, on every outcome.
771
+ # Was a hard-coded 0, so `max_tokens` bounded triage alone.
772
+ spend = usage_spend(result.usage)
753
773
 
754
774
  if result.outcome == OUTCOME_MERGED:
755
- # `run_bug_lane` (`specfuse/loop/bug_lane_run.py`, T04's file) does
756
- # not surface the headless `/fix-bug` session's usage envelope on
757
- # `BugLaneResult` -- reporting the bug lane's real spend needs a
758
- # change there, out of this WU's reach (`specfuse/loop/` is
759
- # off-limits except what T01 declares). Explicit `spend=0` rather
760
- # than a value this module cannot actually measure.
761
775
  return ActionOutcome(
762
776
  status=STATUS_COMPLETED,
763
- detail=f"{result.reason or ''}{profile_note}", spend=0)
777
+ detail=f"{result.reason or ''}{profile_note}", spend=spend)
778
+
779
+ if result.outcome == OUTCOME_SUPERSEDED:
780
+ # #3447: the issue was closed elsewhere and the lane closed its own
781
+ # duplicate PR. Done, with nothing for a human to merge or decide.
782
+ return ActionOutcome(
783
+ status=STATUS_COMPLETED,
784
+ detail=f"{result.reason}; PR #{result.pr_number} closed{profile_note}",
785
+ spend=spend,
786
+ )
764
787
 
765
788
  detail = result.reason if result.reason is not None else result.outcome
766
789
 
@@ -825,6 +848,7 @@ class BugsProvider:
825
848
  detail=f"{detail}{profile_note}",
826
849
  escalation=escalation,
827
850
  environmental=not result.outcome_named,
851
+ spend=spend,
828
852
  )
829
853
 
830
854
  def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
@@ -389,15 +389,23 @@ def _resolve_bugs_preempt(policy_path: Optional[str]) -> bool:
389
389
  return bugs.get("preempt") is True
390
390
 
391
391
 
392
- def _default_runner(argv: list, check: bool = False, timeout: Optional[float] = None):
392
+ def _default_runner(
393
+ argv: list,
394
+ check: bool = False,
395
+ timeout: Optional[float] = None,
396
+ cwd: Optional[str] = None,
397
+ ):
393
398
  """The real subprocess runner. Forwards *timeout* to `subprocess.run`
394
399
  (FEAT-2026-0108/T03) so a caller that bounds one call's wall-clock time --
395
400
  `BugsProvider.execute`'s headless `/fix-bug` dispatch, the one call this
396
401
  exists for -- gets a real `subprocess.TimeoutExpired` when the process
397
402
  outruns it, rather than the session's own turn or wall limit being the
398
- only thing that ever ends it (#3178)."""
403
+ only thing that ever ends it (#3178).
404
+
405
+ Forwards *cwd* for the same dispatch (#3420): the session is started in
406
+ the item's worktree, not the run's own directory."""
399
407
  return subprocess.run(
400
- argv, check=check, capture_output=True, text=True, timeout=timeout
408
+ argv, check=check, capture_output=True, text=True, timeout=timeout, cwd=cwd
401
409
  )
402
410
 
403
411
 
@@ -855,17 +863,15 @@ def run_agent(
855
863
  report=report,
856
864
  )
857
865
 
858
- # The PR listing failing is NOT a refusal: `max_open_prs` deliberately
859
- # reads an unreadable listing as zero rather than firing on a number
860
- # the run could not measure. But that leaves the cap silently disabled
861
- # for this run, so the summary says so rather than the operator finding
862
- # it in the log. Whether it should refuse instead is a live question
863
- # (#3392 item 3) and not decided here.
864
- if snapshot.prs_error:
866
+ # The PR listing failing is NOT a refusal (#3412): work that opens no
867
+ # PR still runs. Under a declared `max_open_prs`, the PR-opening kinds
868
+ # are held back in the dispatch loop below until a refresh reads the
869
+ # listing; without a cap there is nothing to enforce, and the run says
870
+ # only that the listing is unreadable.
871
+ if snapshot.prs_error and agent_policy.resolve_max_open_prs(policy_path) is None:
865
872
  report(
866
- "snapshot: max_open_prs is NOT enforced this run — the PR "
867
- "listing could not be read, so the cap has no number to "
868
- "measure against"
873
+ "snapshot: the PR listing could not be read — no "
874
+ "max_open_prs is declared, so nothing is held back"
869
875
  )
870
876
 
871
877
  budget_kwargs = {}
@@ -916,7 +922,9 @@ def run_agent(
916
922
  #: only so the report fires on each transition rather than every pass:
917
923
  #: `snapshot.prs` is re-read between items (#3338), so a PR merged
918
924
  #: mid-run genuinely lifts this and the operator should see both edges.
919
- open_prs_suppressed = False
925
+ # Why PR-opening kinds are held: "" (not held), "unreadable" or
926
+ # "cap" -- so each change of reason is reported once (#3412).
927
+ open_prs_suppressed = ""
920
928
  #: (signature, consecutive count) for the run-level failure breaker
921
929
  #: (#3343). Reset by an escalation with a different cause and by any
922
930
  #: completed item — a run making progress is not in the state this
@@ -956,26 +964,40 @@ def run_agent(
956
964
  snapshot_stale = False
957
965
 
958
966
  # A failed PR listing leaves `snapshot.prs` empty with
959
- # `prs_error` set. That reads as zero here, deliberately: the cap
960
- # must not fire on a number the run could not measure, and #3338's
961
- # refresh already carries the previous listing forward when it can.
967
+ # `prs_error` set -- not zero open PRs, an unknown number (#3412).
968
+ # Reading it as zero turned the cap off for the run, exactly when
969
+ # the run could see least. Under a cap the PR-opening kinds wait,
970
+ # the way reaching the cap already makes them wait, until a refresh
971
+ # reads the listing. #3338's refresh carries a previously read
972
+ # listing forward, so `prs_error` stays set only while this run has
973
+ # never read one.
962
974
  suppressed_kinds = frozenset()
963
- if _open_pr_cap is not None and len(snapshot.prs) >= _open_pr_cap:
975
+ if _open_pr_cap is not None and snapshot.prs_error:
976
+ suppressed_kinds = PR_OPENING_KINDS
977
+ if open_prs_suppressed != "unreadable":
978
+ report(
979
+ f"max_open_prs NOT measurable — the PR listing could "
980
+ f"not be read ({snapshot.prs_error}); not starting "
981
+ f"{', '.join(sorted(PR_OPENING_KINDS))} items until it "
982
+ f"can be"
983
+ )
984
+ open_prs_suppressed = "unreadable"
985
+ elif _open_pr_cap is not None and len(snapshot.prs) >= _open_pr_cap:
964
986
  suppressed_kinds = PR_OPENING_KINDS
965
- if not open_prs_suppressed:
987
+ if open_prs_suppressed != "cap":
966
988
  report(
967
989
  f"max_open_prs reached — {len(snapshot.prs)} open PR(s) "
968
990
  f"at a cap of {_open_pr_cap}; not starting "
969
991
  f"{', '.join(sorted(PR_OPENING_KINDS))} items until one "
970
992
  f"closes"
971
993
  )
972
- open_prs_suppressed = True
994
+ open_prs_suppressed = "cap"
973
995
  elif open_prs_suppressed:
974
996
  report(
975
997
  f"max_open_prs cleared — {len(snapshot.prs)} open PR(s) "
976
998
  f"against a cap of {_open_pr_cap}"
977
999
  )
978
- open_prs_suppressed = False
1000
+ open_prs_suppressed = ""
979
1001
 
980
1002
  action, a, b = _select_next(
981
1003
  providers,
@@ -1,6 +1,6 @@
1
1
  # Copyright 2026 Specfuse Contributors
2
2
  # Licensed under the Apache License, Version 2.0. See LICENSE.
3
- """`specfuse-backfill-severity` -- amend already-marked, severity-less issues
3
+ """`specfuse-backfill-severity` -- amend already-marked, severity-less bugs
4
4
  with a severity, for issues the normal triage path will never revisit
5
5
  (FEAT-2026-0113).
6
6
 
@@ -15,7 +15,7 @@ script is one deliberate command away, not one argv typo on the binary an
15
15
  unattended run uses.
16
16
 
17
17
  `run_backfill` (T10) is the real run shape: `triage.list_severity_backfill_candidates`
18
- (T08) selects the open, marked, severity-less issues; `labels.read_severity_rubric`
18
+ (T08) selects the open, marked, severity-less bugs; `labels.read_severity_rubric`
19
19
  is read exactly once per run; each candidate is classified through
20
20
  `triage_invoke.build_invocation` + `triage_invoke.classify_severity` -- the same
21
21
  classifier a fresh triage uses, so a low-confidence or out-of-rubric answer fails
@@ -161,7 +161,7 @@ def run_backfill(
161
161
  effort: str = "medium",
162
162
  ) -> dict:
163
163
  """Select, classify, and (only under `apply=True`) write severities for
164
- every open, marked, severity-less issue in `repo` -- the real run shape
164
+ every open, marked, severity-less bug in `repo` -- the real run shape
165
165
  T07's single-issue tracer bullet stood in for.
166
166
 
167
167
  The rubric is read exactly once, before any candidate is even selected:
@@ -289,7 +289,7 @@ def build_parser() -> argparse.ArgumentParser:
289
289
  parser = argparse.ArgumentParser(
290
290
  prog="specfuse-backfill-severity",
291
291
  description=(
292
- "Amend every open, marked, severity-less issue with a severity, "
292
+ "Amend every open, marked, severity-less bug with a severity, "
293
293
  "marker first, label projected after."
294
294
  ),
295
295
  )
@@ -47,7 +47,7 @@ from typing import Callable, Optional
47
47
  from specfuse.loop import agent_policy
48
48
  from specfuse.loop.lint_plan import read_frontmatter
49
49
  from specfuse.loop.plan_baseline import load_plan_graph
50
- from specfuse.loop.triage import parse_marker
50
+ from specfuse.loop.triage import effective_category, parse_marker
51
51
 
52
52
  DEFAULT_ISSUE_LIST_LIMIT = 100
53
53
  DEFAULT_PR_LIST_LIMIT = 100
@@ -166,6 +166,8 @@ def _to_issue_summary(raw: dict) -> IssueSummary:
166
166
  )
167
167
  marker = parse_marker(raw.get("body") or "")
168
168
  category, confidence = marker if marker is not None else (None, None)
169
+ # #3415: an operator's relabel overrides the marker triage wrote.
170
+ category = effective_category(category, labels)
169
171
  return IssueSummary(
170
172
  number=raw.get("number"),
171
173
  title=raw.get("title", ""),
@@ -34,7 +34,7 @@ import json
34
34
  import tempfile
35
35
  import re
36
36
  import time
37
- from dataclasses import dataclass
37
+ from dataclasses import dataclass, replace
38
38
  from typing import Any, Callable, Optional
39
39
 
40
40
  from specfuse.loop.agent_policy import (
@@ -51,13 +51,15 @@ from specfuse.loop.bug_lane import (
51
51
  REASON_DIFF_TOO_LARGE,
52
52
  REASON_JUDGE_PATH_TOUCHED,
53
53
  REASON_NO_TEST_EVIDENCE,
54
+ REASON_RED_ON_BASE_UNVERIFIED,
54
55
  evaluate_merge_guardrails,
55
56
  evaluate_pr_shape_guardrails,
56
57
  judge_paths_touched,
57
58
  )
58
59
  from specfuse.loop.bug_lane_state import GitHubMergeCapState, record_merge
59
60
  from specfuse.loop.labels import provision_labels
60
- from specfuse.loop.red_on_base import select_test_files, test_was_red_on_base
61
+ from specfuse.loop.loop import parse_claude_json_output
62
+ from specfuse.loop.red_on_base import probe_red_on_base, select_test_files
61
63
  from specfuse.loop.triage import parse_marker
62
64
  from specfuse.monitor.diagnosis import DiagnosisParseError
63
65
  from specfuse.monitor.diagnosis import parse as parse_diagnosis
@@ -76,6 +78,7 @@ __all__ = (
76
78
  "OUTCOME_MERGED",
77
79
  "OUTCOME_DECLINED",
78
80
  "OUTCOME_AUTOMERGE_OFF",
81
+ "OUTCOME_SUPERSEDED",
79
82
  "run_bug_lane",
80
83
  "pr_ci_conclusion",
81
84
  "add_guardrail_label",
@@ -104,6 +107,12 @@ OUTCOME_DECLINED = "declined"
104
107
  #: when the guardrails had passed and the dial was the only thing in the way.
105
108
  OUTCOME_AUTOMERGE_OFF = "automerge_off"
106
109
 
110
+ #: The issue was closed while the item ran -- by another PR, or by hand -- so
111
+ #: the lane's PR duplicates work already done and was closed (#3447). Not an
112
+ #: escalation: nothing is left for a human to decide, and asking one to merge
113
+ #: a duplicate of a closed fix was the bug.
114
+ OUTCOME_SUPERSEDED = "superseded"
115
+
107
116
  #: `needs_decision` escalates like the other two: the session opened no PR,
108
117
  #: so there is nothing for the guardrails to evaluate, and what the operator
109
118
  #: owes is an answer rather than a review (#3390).
@@ -182,6 +191,10 @@ class BugLaneResult:
182
191
  #: telling them apart is the difference between "fix this by hand" and
183
192
  #: "push the branch that already exists".
184
193
  unpushed_work: Optional[tuple] = None
194
+ #: The headless session's usage envelope (#3419), `None` when the CLI
195
+ #: printed none. `BugsProvider` turns it into `ActionOutcome.spend`;
196
+ #: without it `max_tokens` never bounded the lane that spends.
197
+ usage: Optional[dict] = None
185
198
 
186
199
 
187
200
  #: How long to wait for a freshly-opened PR's checks to reach a conclusion,
@@ -216,6 +229,10 @@ _BUCKET_PENDING = "pending"
216
229
 
217
230
  _CI_FAILING = "fail"
218
231
 
232
+ #: What `gh pr checks` prints on stderr, with empty stdout and exit 1, for a PR
233
+ #: that has no check runs at all (#3445).
234
+ _NO_CHECKS_REPORTED = "no checks reported"
235
+
219
236
 
220
237
  def _required_checks_verdict(rows: list, required) -> str:
221
238
  """`""` when every required check is present and passing, else why not.
@@ -272,6 +289,12 @@ def _read_ci_conclusion_once(
272
289
  # only a fallback when there is no parseable output.
273
290
  stdout = getattr(result, "stdout", None)
274
291
  if not stdout:
292
+ # #3445: `gh` never prints `[]` for a PR with no check runs. It prints
293
+ # nothing, says `no checks reported on the '<branch>' branch` on stderr
294
+ # and exits 1. That is the "no check registered yet" state below, not
295
+ # an unreadable read, and must not decline as `ci_not_green`.
296
+ if _NO_CHECKS_REPORTED in (getattr(result, "stderr", None) or ""):
297
+ return _CI_PENDING
275
298
  return _CI_UNKNOWN
276
299
 
277
300
  try:
@@ -586,6 +609,70 @@ def _issue_provenance(runner: Callable, repo: str, issue_number: int) -> Optiona
586
609
  return {"kind": "triaged_issue", "ref": str(issue_number)}
587
610
 
588
611
 
612
+ def _superseded_by(
613
+ runner: Callable, repo: str, issue_number: int, own_pr: int
614
+ ) -> tuple[bool, Optional[int]]:
615
+ """Whether *issue_number* was closed by something other than *own_pr*,
616
+ and the PR that closed it when GitHub knows one (#3447).
617
+
618
+ Fails open: an unreadable issue reads as not superseded, so a `gh` hiccup
619
+ leaves the lane exactly where it was before this check existed rather than
620
+ closing a PR that may be the only fix.
621
+ """
622
+ result = runner(
623
+ [
624
+ "gh", "issue", "view", str(issue_number), "--repo", repo,
625
+ "--json", "state,closedByPullRequestsReferences",
626
+ ],
627
+ check=False,
628
+ )
629
+ if getattr(result, "returncode", 1) != 0 or not getattr(result, "stdout", None):
630
+ return False, None
631
+ try:
632
+ data = json.loads(result.stdout)
633
+ except ValueError:
634
+ return False, None
635
+ if not isinstance(data, dict) or data.get("state") != "CLOSED":
636
+ return False, None
637
+ closers = [
638
+ ref.get("number")
639
+ for ref in data.get("closedByPullRequestsReferences") or ()
640
+ if isinstance(ref, dict)
641
+ ]
642
+ closers = [n for n in closers if isinstance(n, int) and not isinstance(n, bool)]
643
+ if own_pr in closers:
644
+ return False, None
645
+ return True, (closers[0] if closers else None)
646
+
647
+
648
+ def _close_superseded(
649
+ runner: Callable,
650
+ repo: str,
651
+ issue_number: int,
652
+ pr_number: int,
653
+ by_pr: Optional[int],
654
+ dispatch_profile: tuple,
655
+ ) -> BugLaneResult:
656
+ """Close the lane's own PR as a duplicate of a fix already done (#3447)."""
657
+ by = f"PR #{by_pr}" if by_pr else "another change"
658
+ runner(
659
+ [
660
+ "gh", "pr", "close", str(pr_number), "--repo", repo,
661
+ "--comment",
662
+ f"Superseded: issue #{issue_number} was closed by {by} while the "
663
+ "bug lane was evaluating this PR. Closing it as a duplicate.",
664
+ ],
665
+ check=False,
666
+ )
667
+ return BugLaneResult(
668
+ outcome=OUTCOME_SUPERSEDED,
669
+ reason=f"superseded by {by}",
670
+ pr_number=pr_number,
671
+ evidence=(f"Issue #{issue_number} was closed by {by}.",),
672
+ dispatch_profile=dispatch_profile,
673
+ )
674
+
675
+
589
676
  #: Branch shape `/fix-bug` uses for an issue it is fixing.
590
677
  _WORK_BRANCH_GLOB = "fix/issue-{number}-*"
591
678
 
@@ -641,9 +728,21 @@ def unpushed_work_for_issue(runner: Callable, issue_number: int) -> Optional[tup
641
728
 
642
729
 
643
730
  def _evidence_for(
644
- reason: str, changed_files: list, diff_lines: int, limits: dict
731
+ reason: str,
732
+ changed_files: list,
733
+ diff_lines: int,
734
+ limits: dict,
735
+ *,
736
+ red_on_base_reason: str = "",
645
737
  ) -> tuple:
646
738
  """Render the measurement behind *reason* as sentences a human can read."""
739
+ if reason == REASON_RED_ON_BASE_UNVERIFIED and red_on_base_reason:
740
+ # #3416: which step of the probe gave up. Without it `unverified` is
741
+ # the one decline nobody can act on short of re-running it by hand.
742
+ return (
743
+ f"The red-on-base probe could not run the PR's tests at the merge "
744
+ f"base: {red_on_base_reason}.",
745
+ )
647
746
  if reason == REASON_JUDGE_PATH_TOUCHED:
648
747
  hits = judge_paths_touched(changed_files)
649
748
  if not hits:
@@ -699,30 +798,37 @@ def _declined(
699
798
  )
700
799
 
701
800
 
702
- def _merge_base_sha(runner: Callable, repo: str, pr_number: int) -> str:
703
- """The commit the PR is measured against, or `""` (#3377).
801
+ def _pr_base_and_head(runner: Callable, repo: str, pr_number: int) -> tuple[str, str]:
802
+ """`(baseRefOid, headRefOid)` for the PR, `""` for whichever is unknown.
704
803
 
705
804
  `baseRefOid` is the tip of the base branch as GitHub sees it now, which is
706
- what the PR would merge into — the tree the fix was written against, and so
707
- the tree its new test must fail on. An empty string makes the probe decline
708
- rather than guess a commit.
805
+ what the PR would merge into -- the tree its new test must fail on
806
+ (#3377). `headRefOid` is the PR's own commit, whose test files the probe
807
+ runs on that tree (#3417). An empty string makes the probe decline rather
808
+ than guess a commit.
709
809
  """
710
810
  try:
711
811
  result = runner(
712
812
  ["gh", "pr", "view", str(pr_number), "--repo", repo,
713
- "--json", "baseRefOid"],
813
+ "--json", "baseRefOid,headRefOid"],
714
814
  check=False,
715
815
  )
716
816
  except Exception: # noqa: BLE001 - a lookup failure declines, never raises
717
- return ""
817
+ return "", ""
718
818
  if getattr(result, "returncode", 1) != 0 or not getattr(result, "stdout", None):
719
- return ""
819
+ return "", ""
720
820
  try:
721
821
  payload = json.loads(result.stdout)
722
822
  except ValueError:
723
- return ""
724
- sha = payload.get("baseRefOid") if isinstance(payload, dict) else None
725
- return sha.strip() if isinstance(sha, str) else ""
823
+ return "", ""
824
+ if not isinstance(payload, dict):
825
+ return "", ""
826
+
827
+ def _sha(key: str) -> str:
828
+ value = payload.get(key)
829
+ return value.strip() if isinstance(value, str) else ""
830
+
831
+ return _sha("baseRefOid"), _sha("headRefOid")
726
832
 
727
833
 
728
834
  def dispatch_profile_for(
@@ -813,6 +919,33 @@ def run_bug_lane(
813
919
  always accurate) and merges only when the dial is `on` *and* the
814
920
  guardrails are eligible -- the module's single merge call site.
815
921
  """
922
+ session: dict = {}
923
+ result = _run_bug_lane(
924
+ runner, repo, issue_number,
925
+ working_dir=working_dir, policy_path=policy_path, now=now,
926
+ ci_sleep=ci_sleep, ci_clock=ci_clock,
927
+ ci_deadline_seconds=ci_deadline_seconds,
928
+ pr_lookup_sleep=pr_lookup_sleep, session=session,
929
+ )
930
+ # #3419: every outcome carries the session's usage, whichever of the
931
+ # lane's many return paths produced it.
932
+ return replace(result, usage=session.get("usage"))
933
+
934
+
935
+ def _run_bug_lane(
936
+ runner: Callable,
937
+ repo: str,
938
+ issue_number: int,
939
+ *,
940
+ working_dir: str,
941
+ policy_path: Any,
942
+ now: Optional[float],
943
+ ci_sleep: Callable,
944
+ ci_clock: Callable,
945
+ ci_deadline_seconds: Optional[float],
946
+ pr_lookup_sleep: Callable,
947
+ session: dict,
948
+ ) -> BugLaneResult:
816
949
  model, effort, profile_source = dispatch_profile_for(
817
950
  runner, repo, issue_number, policy_path
818
951
  )
@@ -820,8 +953,13 @@ def run_bug_lane(
820
953
  argv, prompt = build_invocation(
821
954
  issue_number, repo, working_dir, model=model, effort=effort
822
955
  )
823
- invocation = runner(argv + [prompt], check=False)
824
- session_output = getattr(invocation, "stdout", "") or ""
956
+ # #3419: ask for the CLI's JSON envelope so the session's usage is known.
957
+ # Its `result` is the same final text plain `-p` printed, so everything
958
+ # below reads `session_output` exactly as before.
959
+ invocation = runner(argv + ["--output-format", "json", prompt], check=False)
960
+ session_output, session["usage"] = parse_claude_json_output(
961
+ getattr(invocation, "stdout", "") or ""
962
+ )
825
963
  outcome = classify_outcome(session_output)
826
964
 
827
965
  if outcome in _ESCALATING_OUTCOMES:
@@ -855,6 +993,13 @@ def run_bug_lane(
855
993
  outcome=OUTCOME_DECLINED, reason=_REASON_PR_NOT_FOUND,
856
994
  pr_number=None, dispatch_profile=_profile)
857
995
 
996
+ # #3447: another session may have fixed the issue while this one ran.
997
+ superseded, by_pr = _superseded_by(runner, repo, issue_number, pr_number)
998
+ if superseded:
999
+ return _close_superseded(
1000
+ runner, repo, issue_number, pr_number, by_pr, _profile,
1001
+ )
1002
+
858
1003
  changed_files, diff_lines = _pr_changed_files_and_diff_lines(runner, repo, pr_number)
859
1004
  limits = bug_lane_limits(policy_path)
860
1005
  provenance = _issue_provenance(runner, repo, issue_number)
@@ -902,14 +1047,18 @@ def run_bug_lane(
902
1047
  # test run.
903
1048
  require_red = resolve_require_red_on_base(policy_path)
904
1049
  red_on_base = None
1050
+ red_on_base_reason = ""
905
1051
  if require_red:
906
- red_on_base = test_was_red_on_base(
1052
+ base_sha, head_sha = _pr_base_and_head(runner, repo, pr_number)
1053
+ probe = probe_red_on_base(
907
1054
  runner,
908
- base_sha=_merge_base_sha(runner, repo, pr_number),
1055
+ base_sha=base_sha,
1056
+ head_sha=head_sha,
909
1057
  test_files=select_test_files(changed_files, limits["test_paths"]),
910
1058
  command_template=resolve_red_on_base_command(policy_path),
911
1059
  worktree_root=tempfile.gettempdir(),
912
1060
  )
1061
+ red_on_base, red_on_base_reason = probe.verdict, probe.reason
913
1062
 
914
1063
  decision = evaluate_merge_guardrails(
915
1064
  changed_files=changed_files,
@@ -925,6 +1074,15 @@ def run_bug_lane(
925
1074
  )
926
1075
  dial = resolve_bug_automerge(policy_path)
927
1076
 
1077
+ # #3447: asked again after the CI wait, the longest stretch an item spends
1078
+ # -- the observed duplicates were closed elsewhere during exactly this
1079
+ # window. Neither merging nor escalating a duplicate of a closed fix.
1080
+ superseded, by_pr = _superseded_by(runner, repo, issue_number, pr_number)
1081
+ if superseded:
1082
+ return _close_superseded(
1083
+ runner, repo, issue_number, pr_number, by_pr, _profile,
1084
+ )
1085
+
928
1086
  if dial and decision.eligible:
929
1087
  runner(
930
1088
  ["gh", "pr", "merge", str(pr_number), "--repo", repo, "--squash", "--delete-branch"],
@@ -950,6 +1108,9 @@ def run_bug_lane(
950
1108
  # case returned above.
951
1109
  return _declined(
952
1110
  runner, repo, pr_number, decision.reason, working_dir,
953
- evidence=_evidence_for(decision.reason, changed_files, diff_lines, limits),
1111
+ evidence=_evidence_for(
1112
+ decision.reason, changed_files, diff_lines, limits,
1113
+ red_on_base_reason=red_on_base_reason,
1114
+ ),
954
1115
  dispatch_profile=_profile,
955
1116
  )
@@ -0,0 +1 @@
1
+ 0.27.0