specfuse-loop 0.23.3__tar.gz → 0.25.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 (621) hide show
  1. {specfuse_loop-0.23.3/specfuse_loop.egg-info → specfuse_loop-0.25.0}/PKG-INFO +1 -1
  2. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/pyproject.toml +1 -1
  3. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/triage.py +41 -6
  4. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/run.py +72 -1
  5. specfuse_loop-0.25.0/specfuse/loop/data/VERSION +1 -0
  6. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/methodology.md +40 -2
  7. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/verification.yml.example +13 -0
  8. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/lint_plan.py +1 -1
  9. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/loop.py +159 -8
  10. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/triage.py +23 -3
  11. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
  12. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse_loop.egg-info/SOURCES.txt +7 -0
  13. specfuse_loop-0.25.0/tests/test_blind_run_is_not_drained.py +105 -0
  14. specfuse_loop-0.25.0/tests/test_closing_sessions_on_sonnet.py +28 -0
  15. specfuse_loop-0.25.0/tests/test_dispatch_preamble_rules_pointer.py +32 -0
  16. specfuse_loop-0.25.0/tests/test_dispatch_skills_index.py +105 -0
  17. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_followups_artifact.py +26 -2
  18. specfuse_loop-0.25.0/tests/test_judged_event_points_at_findings.py +137 -0
  19. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_gate_proportionality.py +19 -4
  20. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_auto_archive.py +29 -0
  21. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_close_intermediate.py +2 -2
  22. specfuse_loop-0.25.0/tests/test_spec_work_unit_rules.py +173 -0
  23. specfuse_loop-0.25.0/tests/test_triage_projects_severity_label.py +190 -0
  24. specfuse_loop-0.23.3/specfuse/loop/data/VERSION +0 -1
  25. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/LICENSE +0 -0
  26. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/NOTICE +0 -0
  27. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/README.md +0 -0
  28. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/setup.cfg +0 -0
  29. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/__init__.py +0 -0
  30. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/budget.py +0 -0
  31. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/diagnose_invoke.py +0 -0
  32. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/drafting_answers.py +0 -0
  33. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/drafting_invoke.py +0 -0
  34. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/drafting_questions.py +0 -0
  35. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/driver_command.py +0 -0
  36. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/driver_invoke.py +0 -0
  37. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/invoke.py +0 -0
  38. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/monitoring_read.py +0 -0
  39. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/__init__.py +0 -0
  40. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/answers.py +0 -0
  41. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/bugs.py +0 -0
  42. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/feature.py +0 -0
  43. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/findings_autofix.py +0 -0
  44. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/providers/findings_diagnose.py +0 -0
  45. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/queue_read.py +0 -0
  46. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/repo_detect.py +0 -0
  47. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/severity_backfill.py +0 -0
  48. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/state.py +0 -0
  49. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/triage_invoke.py +0 -0
  50. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/agent/worktree.py +0 -0
  51. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/__init__.py +0 -0
  52. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/_filelock.py +0 -0
  53. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/_miniyaml.py +0 -0
  54. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/_wu_sections.py +0 -0
  55. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/adopt_feature.py +0 -0
  56. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/agent_policy.py +0 -0
  57. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/arm_eval.py +0 -0
  58. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/arm_sweep.py +0 -0
  59. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/arm_txn.py +0 -0
  60. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/bug_lane.py +0 -0
  61. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/bug_lane_run.py +0 -0
  62. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/bug_lane_state.py +0 -0
  63. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/build_provenance.py +0 -0
  64. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/caller_check.py +0 -0
  65. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/changelog.py +0 -0
  66. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/closing_requirements.py +0 -0
  67. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/cost.py +0 -0
  68. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/criteria_state.py +0 -0
  69. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  70. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  71. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  72. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  73. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  74. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  75. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/getting-started.md +0 -0
  76. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/glossary.md +0 -0
  77. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/docs/skills.md +0 -0
  78. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/gitignore.snippet +0 -0
  79. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  80. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  81. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/monitoring.yml.example +0 -0
  82. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/roadmap.template.md +0 -0
  83. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/close-discipline.md +0 -0
  84. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  85. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  86. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/human-output.md +0 -0
  87. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/never-touch.md +0 -0
  88. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  89. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  90. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/result-contract.md +0 -0
  91. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  92. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  93. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/rules-local/README.md +0 -0
  94. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  95. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/schemas/event.schema.json +0 -0
  96. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  97. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  98. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  99. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  100. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  101. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/templates/GATE.template.md +0 -0
  102. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  103. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  104. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/templates/WU.template.md +0 -0
  105. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  106. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/decisions_format.py +0 -0
  107. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/driver_edit.py +0 -0
  108. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/escalation.py +0 -0
  109. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/events_stats.py +0 -0
  110. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/gate_commands.py +0 -0
  111. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/gate_eval.py +0 -0
  112. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/gh_backend.py +0 -0
  113. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/gh_features.py +0 -0
  114. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/heartbeat.py +0 -0
  115. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/judge.py +0 -0
  116. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/labels.py +0 -0
  117. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/learnings_distil.py +0 -0
  118. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/learnings_query.py +0 -0
  119. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/lint_closing.py +0 -0
  120. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/lint_monitoring.py +0 -0
  121. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/lint_roadmap.py +0 -0
  122. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/notify.py +0 -0
  123. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/notify_escalation.py +0 -0
  124. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/notify_sla.py +0 -0
  125. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/plan_baseline.py +0 -0
  126. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/policy_proposals.py +0 -0
  127. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/policy_review.py +0 -0
  128. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/prerun.py +0 -0
  129. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/prerun_capture.py +0 -0
  130. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/promotion.py +0 -0
  131. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/rearm_migration.py +0 -0
  132. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/red_on_base.py +0 -0
  133. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/replay_spin.py +0 -0
  134. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/roadmap_archive.py +0 -0
  135. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/scaffold.py +0 -0
  136. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/upgrade_merge_gate.py +0 -0
  137. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/loop/validate_event.py +0 -0
  138. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/__init__.py +0 -0
  139. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/adapters.py +0 -0
  140. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/artifact.py +0 -0
  141. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/autofix.py +0 -0
  142. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/autofix_invoke.py +0 -0
  143. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/autofix_run.py +0 -0
  144. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/autofix_state.py +0 -0
  145. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/cli.py +0 -0
  146. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/diagnose_cli.py +0 -0
  147. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/diagnosis.py +0 -0
  148. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/fingerprint.py +0 -0
  149. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/issues.py +0 -0
  150. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/providers/__init__.py +0 -0
  151. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
  152. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  153. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  154. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/redaction.py +0 -0
  155. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse/monitor/schedule.py +0 -0
  156. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  157. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
  158. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse_loop.egg-info/requires.txt +0 -0
  159. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/specfuse_loop.egg-info/top_level.txt +0 -0
  160. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_abandon_feature_skill.py +0 -0
  161. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_abandoned_work_reported.py +0 -0
  162. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
  163. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_adopt_feature.py +0 -0
  164. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_budget.py +0 -0
  165. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_bug_selection_dedup.py +0 -0
  166. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_default_features_root.py +0 -0
  167. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_driver_command.py +0 -0
  168. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_driver_invoke.py +0 -0
  169. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_driver_restart.py +0 -0
  170. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_escalation_on_issue.py +0 -0
  171. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_escalation_run_state.py +0 -0
  172. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_findings_seam.py +0 -0
  173. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_invoke_timeout.py +0 -0
  174. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_invoke_usage.py +0 -0
  175. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_item_worktree.py +0 -0
  176. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_lock.py +0 -0
  177. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_policy_key_ownership.py +0 -0
  178. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_policy_queue.py +0 -0
  179. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_policy_schema.py +0 -0
  180. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_policy_triage_dial.py +0 -0
  181. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_answers.py +0 -0
  182. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_bugs.py +0 -0
  183. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_feature.py +0 -0
  184. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_findings_autofix.py +0 -0
  185. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
  186. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_isolation.py +0 -0
  187. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_provider_triage.py +0 -0
  188. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_queue_read.py +0 -0
  189. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_relays_driver_output.py +0 -0
  190. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_repo_detect.py +0 -0
  191. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_run.py +0 -0
  192. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_run_progress.py +0 -0
  193. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_seam.py +0 -0
  194. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_snapshot_refresh.py +0 -0
  195. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_agent_state.py +0 -0
  196. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_answer_escalation_skill.py +0 -0
  197. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_app_insights_adapters.py +0 -0
  198. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_archive_ref_reconciliation.py +0 -0
  199. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_eval.py +0 -0
  200. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_eval_lint_class.py +0 -0
  201. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_eval_wiring.py +0 -0
  202. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  203. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_sweep.py +0 -0
  204. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_sweep_gate.py +0 -0
  205. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_txn.py +0 -0
  206. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_arm_wiring.py +0 -0
  207. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_artifact_redaction.py +0 -0
  208. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attempt_note_untracked_files.py +0 -0
  209. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attempt_outcome_contract.py +0 -0
  210. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attempt_outcome_emission.py +0 -0
  211. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attention_nonwriting_guard.py +0 -0
  212. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attention_silence_section.py +0 -0
  213. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attention_skill_structure.py +0 -0
  214. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_attribution_halt_claims.py +0 -0
  215. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autoclose_deferral_visibility.py +0 -0
  216. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autoclose_gate_note.py +0 -0
  217. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  218. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autofix_invoke.py +0 -0
  219. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autofix_live.py +0 -0
  220. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autofix_predicate.py +0 -0
  221. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autofix_run.py +0 -0
  222. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autofix_state.py +0 -0
  223. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autosync.py +0 -0
  224. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autosync_consent.py +0 -0
  225. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autosync_firstrun.py +0 -0
  226. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autosync_no_cwd_leak.py +0 -0
  227. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_autosync_plugin.py +0 -0
  228. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_azure_credential_resolution.py +0 -0
  229. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_backend.py +0 -0
  230. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_baseline_escalation_message.py +0 -0
  231. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_baseline_persistence.py +0 -0
  232. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_baseline_probe.py +0 -0
  233. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_baseline_provenance.py +0 -0
  234. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_baseline_tree_hash_key.py +0 -0
  235. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bash_routing.py +0 -0
  236. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bats_suites_gated.py +0 -0
  237. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_binary_verdict.py +0 -0
  238. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_binding_block_advisory.py +0 -0
  239. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_binding_block_allocation.py +0 -0
  240. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_binding_block_budget.py +0 -0
  241. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  242. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  243. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bookkeeping_no_verify.py +0 -0
  244. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  245. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_ci_pending.py +0 -0
  246. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_guardrails.py +0 -0
  247. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
  248. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_labels_registered.py +0 -0
  249. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_min_severity.py +0 -0
  250. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_policy_contract.py +0 -0
  251. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
  252. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  253. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_run.py +0 -0
  254. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_state.py +0 -0
  255. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  256. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_bug_lane_test_paths.py +0 -0
  257. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_build_provenance.py +0 -0
  258. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_caller_check_ratchet.py +0 -0
  259. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_changed_file_test_selection.py +0 -0
  260. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_changelog_append_only.py +0 -0
  261. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_changelog_collection.py +0 -0
  262. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_changelog_release_wiring.py +0 -0
  263. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_changelog_schema.py +0 -0
  264. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_ci_conclusion_wait.py +0 -0
  265. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_ci_runs_every_gate.py +0 -0
  266. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_claude_resolution.py +0 -0
  267. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_close_b_pending_learnings.py +0 -0
  268. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_close_records_feature_oracle.py +0 -0
  269. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_close_wu_self_reference.py +0 -0
  270. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_closing_deliverable_guard.py +0 -0
  271. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_closing_guard_contracts.py +0 -0
  272. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_closing_guard_prediction.py +0 -0
  273. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_closing_learnings_destination.py +0 -0
  274. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_closing_requirements.py +0 -0
  275. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
  276. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_conditional_reflection.py +0 -0
  277. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_console_encoding.py +0 -0
  278. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_convergent_iteration.py +0 -0
  279. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_convergent_wu_lint.py +0 -0
  280. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_correlation_id_override.py +0 -0
  281. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_correlation_id_two_surfaces.py +0 -0
  282. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_cost_lifetime.py +0 -0
  283. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_cost_tracking.py +0 -0
  284. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_criteria_per_gate_lint.py +0 -0
  285. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_criteria_state.py +0 -0
  286. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_criteria_worklist.py +0 -0
  287. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_decision_citation_lint.py +0 -0
  288. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_decision_followups_discharged.py +0 -0
  289. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_decision_override_lint.py +0 -0
  290. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_decisions_format.py +0 -0
  291. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_default_severity_aliases.py +0 -0
  292. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_degraded_oracle.py +0 -0
  293. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_deliverable_missing_note.py +0 -0
  294. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_deliverable_presence_gate.py +0 -0
  295. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
  296. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_derive_agent_policy_skill.py +0 -0
  297. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_derive_monitoring_discovery.py +0 -0
  298. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
  299. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_design_for_diagnosis_rule.py +0 -0
  300. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_deterministic_refusal_repeat.py +0 -0
  301. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_diagnose_headless.py +0 -0
  302. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_diagnose_issue_skill.py +0 -0
  303. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_diagnosis.py +0 -0
  304. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
  305. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_dispatch_model_from_fix_scope.py +0 -0
  306. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_dispatch_skeleton.py +0 -0
  307. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_distilled_accept_step.py +0 -0
  308. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_doctor.py +0 -0
  309. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_doctor_command_preflight.py +0 -0
  310. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_done_feature_gates.py +0 -0
  311. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_draft_feature_answers_mode.py +0 -0
  312. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_drafting_answer_gate.py +0 -0
  313. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_drafting_invoke.py +0 -0
  314. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_drafting_question_issue.py +0 -0
  315. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_drafting_questions.py +0 -0
  316. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_drafting_reply_shape.py +0 -0
  317. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_edit_detection.py +0 -0
  318. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  319. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_integration.py +0 -0
  320. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_lock.py +0 -0
  321. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_module_surface.py +0 -0
  322. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
  323. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_restart_halt_wiring.py +0 -0
  324. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_restart_hold.py +0 -0
  325. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_staleness_gate_summary.py +0 -0
  326. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_driver_staleness_warning.py +0 -0
  327. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
  328. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_duration_tracking.py +0 -0
  329. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_empty_files_escalation.py +0 -0
  330. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_ensure_feature_branch.py +0 -0
  331. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_ensure_feature_branch_base.py +0 -0
  332. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_escalation_assignee_policy.py +0 -0
  333. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_escalation_contract.py +0 -0
  334. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_escalation_crash_containment.py +0 -0
  335. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_escalation_dedup_search.py +0 -0
  336. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_escalation_emit.py +0 -0
  337. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_event_gate_full_envelope.py +0 -0
  338. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_events_redaction.py +0 -0
  339. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_events_stats.py +0 -0
  340. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_extra_gates.py +0 -0
  341. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_failure_artifact_model.py +0 -0
  342. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_failure_signature_fail_prefix.py +0 -0
  343. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
  344. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_branch_auto_sync.py +0 -0
  345. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_branch_squash_merge.py +0 -0
  346. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_branch_stale_base.py +0 -0
  347. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_id_resolution.py +0 -0
  348. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_oracle_e2e.py +0 -0
  349. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  350. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_feature_review_accumulation.py +0 -0
  351. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_filelock_portable.py +0 -0
  352. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_files_changed_auto_repair.py +0 -0
  353. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_find_pr_no_search_index.py +0 -0
  354. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_fingerprint.py +0 -0
  355. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_fix_bug_diff_self_check.py +0 -0
  356. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_fix_bug_headless.py +0 -0
  357. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_fix_bug_reads_comments.py +0 -0
  358. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_fold_contract_documented.py +0 -0
  359. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_force_full_close.py +0 -0
  360. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
  361. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_broad_run.py +0 -0
  362. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_budget.py +0 -0
  363. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_budget_post_dispatch.py +0 -0
  364. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_commands_needs.py +0 -0
  365. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_eval.py +0 -0
  366. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_eval_calibration.py +0 -0
  367. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  368. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_eval_repo_flag.py +0 -0
  369. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_eval_terminal_wiring.py +0 -0
  370. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gate_report_verdict.py +0 -0
  371. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gh_backend.py +0 -0
  372. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gh_features.py +0 -0
  373. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_git_env_isolation.py +0 -0
  374. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_gitleaks_pinning.py +0 -0
  375. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_groom_backlog_skill.py +0 -0
  376. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_guard_refusal_failure_class.py +0 -0
  377. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  378. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_guard_repair_e2e.py +0 -0
  379. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_guard_repair_prompt.py +0 -0
  380. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_hashed_denylist.py +0 -0
  381. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_hashed_denylist_ci.py +0 -0
  382. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_heartbeat.py +0 -0
  383. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_heartbeat_adapter.py +0 -0
  384. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_human_output_rule.py +0 -0
  385. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_human_work_unit.py +0 -0
  386. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_hygiene_boundary_guidance.py +0 -0
  387. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_hyphenated_gate_name.py +0 -0
  388. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_ignored_cleanup_before_probe.py +0 -0
  389. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_init_integration.py +0 -0
  390. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_installed_copy_driver_e2e.py +0 -0
  391. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_interpreter_normalization.py +0 -0
  392. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_judge_close_path.py +0 -0
  393. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_judge_cost.py +0 -0
  394. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_judge_cost_fold_committed.py +0 -0
  395. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_judge_module.py +0 -0
  396. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_judge_path_registry.py +0 -0
  397. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_judge_terminal_flip_state.py +0 -0
  398. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_label_description_length.py +0 -0
  399. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_label_provisioning_runner_contract.py +0 -0
  400. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_label_registry.py +0 -0
  401. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_label_registry_covers_consumers.py +0 -0
  402. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_language_aware_narrow_selection.py +0 -0
  403. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lazy_baseline_e2e.py +0 -0
  404. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_leak_findings_redaction.py +0 -0
  405. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_leak_scan.py +0 -0
  406. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_leak_scan_content.py +0 -0
  407. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
  408. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_learnings_distil_cli.py +0 -0
  409. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_learnings_query.py +0 -0
  410. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_learnings_staging.py +0 -0
  411. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
  412. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_learnings_weights.py +0 -0
  413. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  414. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lifecycle_integration.py +0 -0
  415. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lifetime_accounting.py +0 -0
  416. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_ac_observable.py +0 -0
  417. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_bare_produces_path.py +0 -0
  418. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_boundary_consistency.py +0 -0
  419. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_boundary_extraction.py +0 -0
  420. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_close_intermediate.py +0 -0
  421. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  422. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_close_wu.py +0 -0
  423. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_closing.py +0 -0
  424. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_closing_criteria.py +0 -0
  425. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
  426. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_correlation_id.py +0 -0
  427. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  428. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  429. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_feature_oracle_declared.py +0 -0
  430. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_monitoring.py +0 -0
  431. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_oracle_env.py +0 -0
  432. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_plan_contract_fields.py +0 -0
  433. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_plan_errors.py +0 -0
  434. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_plan_next_draft.py +0 -0
  435. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_plan_relative_import.py +0 -0
  436. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
  437. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_planning_sections.py +0 -0
  438. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_produces_driver_helper.py +0 -0
  439. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_produces_satisfiability.py +0 -0
  440. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_roadmap.py +0 -0
  441. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_sections.py +0 -0
  442. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  443. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_lint_verification_reference.py +0 -0
  444. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_live_tests_opt_in.py +0 -0
  445. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_load_bearing_close_lint.py +0 -0
  446. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_attempt_evidence.py +0 -0
  447. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_caveman_preamble.py +0 -0
  448. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_criteria_skeleton.py +0 -0
  449. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_criteria_survival.py +0 -0
  450. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_defaults_by_type.py +0 -0
  451. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_effort.py +0 -0
  452. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_failure_note_cap.py +0 -0
  453. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_files_changed_guard.py +0 -0
  454. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_gate_budget.py +0 -0
  455. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_gate_needs.py +0 -0
  456. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_model_alias.py +0 -0
  457. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_orchestration.py +0 -0
  458. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_post_pass_invariant.py +0 -0
  459. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_reset_preserving_events.py +0 -0
  460. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_smoke_runner.py +0 -0
  461. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_unsandboxed.py +0 -0
  462. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_worklist_injection.py +0 -0
  463. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_loop_zero_token_guard.py +0 -0
  464. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  465. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_marker_dual_shape.py +0 -0
  466. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_maven_failure_excerpt.py +0 -0
  467. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_max_attempts_configurable.py +0 -0
  468. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_max_open_prs.py +0 -0
  469. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_migrate_legacy.py +0 -0
  470. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_miniyaml_equivalence.py +0 -0
  471. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_miniyaml_negative.py +0 -0
  472. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitor_cli.py +0 -0
  473. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitor_issue_lifecycle.py +0 -0
  474. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitor_runner_surfaces.py +0 -0
  475. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  476. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitoring_cron_dialect.py +0 -0
  477. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitoring_example.py +0 -0
  478. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitoring_fenced_blocks.py +0 -0
  479. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_monitoring_seed.py +0 -0
  480. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_narrow_fallback_warning.py +0 -0
  481. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_needs_decision_outcome.py +0 -0
  482. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_no_pytest_subprocess.py +0 -0
  483. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_notify.py +0 -0
  484. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_notify_escalation.py +0 -0
  485. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_notify_sla.py +0 -0
  486. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_operator_escalation_rule.py +0 -0
  487. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_oracle_set_declared.py +0 -0
  488. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_oracle_unrunnable_attribution.py +0 -0
  489. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_per_run_item_cap_disclosure.py +0 -0
  490. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pick_feature_skill.py +0 -0
  491. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pin_honesty_and_integrity.py +0 -0
  492. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pin_marker_not_inherited.py +0 -0
  493. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pin_marker_spawn_sites.py +0 -0
  494. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_plan_baseline.py +0 -0
  495. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_planned_cost_lint.py +0 -0
  496. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_planning_cost_floor.py +0 -0
  497. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_plugin_prose_cross_refs.py +0 -0
  498. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_plugin_qualified_dispatch.py +0 -0
  499. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_policy_budgets_applied.py +0 -0
  500. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_policy_proposals.py +0 -0
  501. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_policy_review.py +0 -0
  502. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pr_checks_json_field.py +0 -0
  503. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pr_reference_in_title.py +0 -0
  504. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  505. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_prep_halt_operator_surface.py +0 -0
  506. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_prepare_feature.py +0 -0
  507. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_prepare_scaffold_sync.py +0 -0
  508. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_prerun_capture.py +0 -0
  509. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_prerun_oracles.py +0 -0
  510. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_prerun_wiring.py +0 -0
  511. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produced_document_verdict_guard.py +0 -0
  512. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_bracket_literal.py +0 -0
  513. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_bracket_wildcard.py +0 -0
  514. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_field.py +0 -0
  515. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_in_diff.py +0 -0
  516. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_justification.py +0 -0
  517. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_recursive_glob.py +0 -0
  518. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_semantics_unified.py +0 -0
  519. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_produces_shape.py +0 -0
  520. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_progress_forward_note.py +0 -0
  521. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_progress_lines_end_to_end.py +0 -0
  522. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_promotion_ledger.py +0 -0
  523. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_provider_escalation_traces.py +0 -0
  524. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_provision_labels.py +0 -0
  525. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_queue_stalled_adapter.py +0 -0
  526. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  527. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_rearm_fold_marker.py +0 -0
  528. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_rearm_migration.py +0 -0
  529. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_red_baseline_never_reused.py +0 -0
  530. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_red_on_base_guardrail.py +0 -0
  531. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_red_on_base_probe.py +0 -0
  532. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_redaction_windows_home.py +0 -0
  533. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_reflection_required_signals.py +0 -0
  534. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_replan_end_to_end.py +0 -0
  535. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_replan_event_emission.py +0 -0
  536. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_replan_note_collision.py +0 -0
  537. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_replan_trigger.py +0 -0
  538. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_replan_turn_contract.py +0 -0
  539. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_replay_spin.py +0 -0
  540. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_required_checks.py +0 -0
  541. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_resolve_base.py +0 -0
  542. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_restart_cap_counts_progress.py +0 -0
  543. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_result_block.py +0 -0
  544. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_result_block_audience.py +0 -0
  545. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_resume_command_build.py +0 -0
  546. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  547. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_add_skill.py +0 -0
  548. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_archive_cli.py +0 -0
  549. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_archive_shim.py +0 -0
  550. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_archive_skill.py +0 -0
  551. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  552. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_link_gate.py +0 -0
  553. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_roadmap_row_parser.py +0 -0
  554. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_ruff_verdict_lines.py +0 -0
  555. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_rule_import_convergence.py +0 -0
  556. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_run_level_failure_breaker.py +0 -0
  557. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_data_in_sync.py +0 -0
  558. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_doc_hygiene.py +0 -0
  559. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_docs.py +0 -0
  560. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_init.py +0 -0
  561. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_label_provisioning.py +0 -0
  562. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_manifest.py +0 -0
  563. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_resources.py +0 -0
  564. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_seed_sanity.py +0 -0
  565. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_skew_halts.py +0 -0
  566. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_upgrade.py +0 -0
  567. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_scaffold_wiring.py +0 -0
  568. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_schedule_dialect.py +0 -0
  569. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_seed_documents_prerun_keys.py +0 -0
  570. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_service_bus_dlq_adapter.py +0 -0
  571. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_session_env_preflight.py +0 -0
  572. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_aliases.py +0 -0
  573. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_backfill_apply.py +0 -0
  574. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_backfill_end_to_end.py +0 -0
  575. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_backfill_limit.py +0 -0
  576. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_backfill_marker.py +0 -0
  577. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_backfill_run.py +0 -0
  578. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_label_projection.py +0 -0
  579. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_reconciliation.py +0 -0
  580. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_severity_rubric.py +0 -0
  581. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_shipped_templates_parse.py +0 -0
  582. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_skill_discovery_links.py +0 -0
  583. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_skills_vendored_in_sync.py +0 -0
  584. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_spinning_rearm_gate.py +0 -0
  585. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_spinout_brief_end_to_end.py +0 -0
  586. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_spinout_brief_replan_option.py +0 -0
  587. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_squash_commit_hook_crash.py +0 -0
  588. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_stop_rationale_captured.py +0 -0
  589. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_stranded_halt_cause.py +0 -0
  590. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_surefire_run_level_signature.py +0 -0
  591. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_surefire_verdict_lines.py +0 -0
  592. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_template_closing_shapes.py +0 -0
  593. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_terminal_flip_ownership.py +0 -0
  594. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_terminal_flips.py +0 -0
  595. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_terminal_gate_message.py +0 -0
  596. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_tiered_verification_e2e.py +0 -0
  597. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_timeout_kill_portable.py +0 -0
  598. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage.py +0 -0
  599. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage_apply.py +0 -0
  600. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage_severity_classify.py +0 -0
  601. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage_severity_end_to_end.py +0 -0
  602. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage_severity_write.py +0 -0
  603. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage_skill_contract.py +0 -0
  604. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_triage_skips_agent_escalations.py +0 -0
  605. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_tsc_verdict_lines.py +0 -0
  606. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_umbrella_version_resolution.py +0 -0
  607. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_unterminated_frontmatter.py +0 -0
  608. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_untracked_feature_folder.py +0 -0
  609. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_untracked_file_absorption.py +0 -0
  610. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_upgrade_integration.py +0 -0
  611. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_upgrade_merge_gate.py +0 -0
  612. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_validate_event.py +0 -0
  613. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_validate_event_driver_types.py +0 -0
  614. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_verdict_coupling.py +0 -0
  615. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_verify_empty_gate_set.py +0 -0
  616. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_version_consistency.py +0 -0
  617. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_version_skew.py +0 -0
  618. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_windows_gate_exec.py +0 -0
  619. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_worktree_restore.py +0 -0
  620. {specfuse_loop-0.23.3 → specfuse_loop-0.25.0}/tests/test_wu_execution_metadata.py +0 -0
  621. {specfuse_loop-0.23.3 → specfuse_loop-0.25.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.23.3
3
+ Version: 0.25.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.23.3"
7
+ version = "0.25.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"
@@ -40,7 +40,11 @@ from specfuse.agent.invoke import run_claude, usage_spend
40
40
  from specfuse.agent.state import AgentSnapshot
41
41
  from specfuse.agent.triage_invoke import build_invocation, classify_result, classify_severity
42
42
  from specfuse.loop.escalation import CATEGORY_LABELS
43
- from specfuse.loop.labels import read_severity_rubric
43
+ from specfuse.loop.labels import (
44
+ list_existing_labels,
45
+ read_severity_rubric,
46
+ severity_label_projection,
47
+ )
44
48
  from specfuse.loop.triage import CATEGORIES, apply_triage, list_untriaged
45
49
 
46
50
  _ITEM_ID_PREFIX = "triage-"
@@ -188,6 +192,7 @@ class TriageProvider:
188
192
  self._rows: dict = {}
189
193
  self._auto = False
190
194
  self._rubric: Any = _RUBRIC_UNSET
195
+ self._projection: Any = _RUBRIC_UNSET
191
196
 
192
197
  def _rubric_for_run(self) -> dict:
193
198
  """The repository's severity rubric, read at most once per run
@@ -195,14 +200,40 @@ class TriageProvider:
195
200
  `execute()` call after an `advertise()`, so a run that classifies
196
201
  nothing issues no `gh label list` at all."""
197
202
  if self._rubric is _RUBRIC_UNSET:
198
- self._rubric = read_severity_rubric(
199
- self._working_dir, runner=self._runner, repo=self._repo
200
- )
203
+ self._read_labels_once()
201
204
  return self._rubric
202
205
 
206
+ def _projection_for_run(self) -> dict:
207
+ """`{canonical value: the label this repository spells it with}` (#3386).
208
+
209
+ Triage wrote canonical labels a repository may never have defined; the
210
+ write failed, and because the bug lane reads severity from the label
211
+ rather than the marker, triage removed from the lane the issues it had
212
+ just classified for it. Derived from the same listing as the rubric —
213
+ one `gh label list` per run, which is the invariant that accessor
214
+ already carried."""
215
+ if self._projection is _RUBRIC_UNSET:
216
+ self._read_labels_once()
217
+ return self._projection
218
+
219
+ def _read_labels_once(self) -> None:
220
+ existing, _reason = list_existing_labels(
221
+ self._working_dir, runner=self._runner, repo=self._repo
222
+ )
223
+ self._rubric = read_severity_rubric(
224
+ self._working_dir, runner=self._runner, repo=self._repo,
225
+ existing=existing,
226
+ )
227
+ try:
228
+ names = [item["name"] for item in (existing or [])]
229
+ except (TypeError, KeyError):
230
+ names = []
231
+ self._projection = severity_label_projection(names)
232
+
203
233
  def advertise(self, snapshot: AgentSnapshot) -> Sequence[ActionItem]:
204
234
  self._auto = snapshot.triage_auto
205
235
  self._rubric = _RUBRIC_UNSET
236
+ self._projection = _RUBRIC_UNSET
206
237
  rows = list_untriaged(self._runner, self._repo)
207
238
 
208
239
  self._rows = {}
@@ -250,7 +281,9 @@ class TriageProvider:
250
281
  "labels": row.get("labels") or [],
251
282
  }
252
283
  ]
253
- results = apply_triage(self._runner, self._repo, decisions, auto=self._auto)
284
+ results = apply_triage(
285
+ self._runner, self._repo, decisions, auto=self._auto,
286
+ label_projection=self._projection_for_run())
254
287
  row_result = results[0]
255
288
  if row_result.get("label_written"):
256
289
  return ActionOutcome(
@@ -337,7 +370,9 @@ class TriageProvider:
337
370
  "severity": severity,
338
371
  }
339
372
  ]
340
- results = apply_triage(self._runner, self._repo, decisions, auto=self._auto)
373
+ results = apply_triage(
374
+ self._runner, self._repo, decisions, auto=self._auto,
375
+ label_projection=self._projection_for_run())
341
376
  row_result = results[0]
342
377
 
343
378
  if row_result.get("skipped"):
@@ -76,6 +76,13 @@ STATUS_ESCALATED = "escalated"
76
76
  #: (FEAT-2026-0108/T02). Not one of `budget.py`'s stop reasons -- nothing was
77
77
  #: budgeted, because nothing was dispatched.
78
78
  STOP_DIRTY_TREE = "dirty_tree"
79
+ #: The run could not read the queue it was meant to work through (#3392).
80
+ #: Distinct from `STOP_DRAINED`, which means the opposite: a drained run saw
81
+ #: everything there was and finished it. Reported identically, they are
82
+ #: indistinguishable in the summary — the part an operator reads and the only
83
+ #: part automation parses — so a nightly run whose credentials broke looks like
84
+ #: a quiet backlog.
85
+ STOP_SNAPSHOT_UNREADABLE = "snapshot_unreadable"
79
86
 
80
87
  KIND_BUG = "bug"
81
88
  KIND_FEATURE = "feature"
@@ -706,6 +713,43 @@ def _refuse_dirty_tree(reason: str, report: Callable[[str], None]) -> RunSummary
706
713
  )
707
714
 
708
715
 
716
+ def _refuse_unreadable_snapshot(reason: str, *, report) -> "RunSummary":
717
+ """Refuse a run whose issue listing failed, rather than draining it (#3392).
718
+
719
+ Every provider that matters reads `snapshot.issues`, so a run without it
720
+ can do nothing except mislead. Shaped like `_refuse_dirty_tree`: zero
721
+ attempted, zero completed, one escalation carrying the reason, and a
722
+ distinct stop reason so the summary cannot be mistaken for a healthy run
723
+ against an empty queue.
724
+
725
+ Refusing is the conservative direction here for the same reason it is
726
+ there. A blind run that dispatches nothing has cost nothing; a blind run
727
+ reported as `drained` costs the operator their belief that the queue is
728
+ empty.
729
+ """
730
+ report(f"run refused to start — {reason}")
731
+ return RunSummary(
732
+ items_attempted=0,
733
+ items_completed=0,
734
+ items_escalated=1,
735
+ stop_reason=STOP_SNAPSHOT_UNREADABLE,
736
+ elapsed_minutes=0.0,
737
+ tokens_spent=0,
738
+ escalations=(Escalation(item_id="run:snapshot", reason=reason),),
739
+ )
740
+
741
+
742
+ def exit_code_for(stop_reason: str) -> int:
743
+ """The process exit code a run ending in *stop_reason* should report.
744
+
745
+ Non-zero only for a refusal — a run that did not happen. Every outcome,
746
+ including `drained`, keeps 0: those are runs that ran. Narrow on purpose,
747
+ so a new stop reason does not silently become a failing exit code for
748
+ whatever automation is watching.
749
+ """
750
+ return 1 if stop_reason == STOP_SNAPSHOT_UNREADABLE else 0
751
+
752
+
709
753
  def _dirty_tree_reason(paths) -> str:
710
754
  shown = ", ".join(paths[:10])
711
755
  if len(paths) > 10:
@@ -799,6 +843,31 @@ def run_agent(
799
843
  if error:
800
844
  report(f"snapshot: {section} unreadable — {error}")
801
845
 
846
+ # #3392: an unreadable issue listing is not an empty queue. Every
847
+ # provider that matters reads `snapshot.issues`, so continuing here
848
+ # finds no candidates and stops with `drained` — "worked through
849
+ # everything there was" reported for "could not see anything". Measured
850
+ # with nine dispatchable bugs in the queue at the time.
851
+ if snapshot.issues_error:
852
+ return _refuse_unreadable_snapshot(
853
+ f"the issue listing failed, so the queue could not be read: "
854
+ f"{snapshot.issues_error}",
855
+ report=report,
856
+ )
857
+
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:
865
+ 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"
869
+ )
870
+
802
871
  budget_kwargs = {}
803
872
  if pause_marker is not None:
804
873
  budget_kwargs["pause_marker"] = pause_marker
@@ -1294,7 +1363,9 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1294
1363
  worktree.restore_branch(started_on, report=_default_reporter)
1295
1364
 
1296
1365
  print(_format_summary(summary))
1297
- return 0
1366
+ # #3392: a refusal exits non-zero so a scheduled run's exit status is
1367
+ # self-describing without its log. Outcomes — `drained` included — keep 0.
1368
+ return exit_code_for(summary.stop_reason)
1298
1369
 
1299
1370
 
1300
1371
  if __name__ == "__main__":
@@ -0,0 +1 @@
1
+ 0.25.0
@@ -454,6 +454,27 @@ separately carry `auto_repaired_files_changed` in `extras`: the list of
454
454
  declared-but-unchanged `files_changed` paths the driver dropped on the
455
455
  WU's behalf rather than failing the attempt over.
456
456
 
457
+ **Dispatch without the skills index (#3423).** A dispatched session runs
458
+ `claude -p --disable-slash-commands` by default: a work unit invokes no
459
+ skill, and the project's skills index (34 skills, 1,534 words of descriptions
460
+ in the loop repository) is fixed context carried in every turn's cache read.
461
+ `defaults.dispatch_skills: true` in `verification.yml` restores the index for
462
+ a project whose units do call `/skill-name` in-session.
463
+
464
+ **The post-merge checklist travels with the archive (#3424).** A `met`
465
+ terminal close no longer files PLAN.md's `## Post-merge checklist` as a
466
+ `specfuse:post-merge` issue (ten filed across two repositories, eight never
467
+ actioned); `auto_archive_feature` appends it under the feature's archived
468
+ detail section in `roadmap-archive.md`. `defaults.post_merge_issue: true` in
469
+ `verification.yml` restores the issue.
470
+
471
+ **Closing sessions run on sonnet (#3425).** `close`, `close-intermediate`
472
+ and `plan-next` default to `sonnet` at effort `high`, the same model as the
473
+ judge that checks them (lower-only, about $0.46 per close). Opus was 56% of
474
+ all spend while implementation ran on sonnet. A unit's own
475
+ `model:` still overrides the default, and every `judged` event carries
476
+ `close_model` so judge agreement can be read per close model.
477
+
457
478
  Retention interacts with spinning detection, not around it:
458
479
  `detect_deterministic_refusal_repeat` (#1415) escalates a WU to
459
480
  `blocked_human` when two consecutive attempts emit an identical refusal
@@ -579,7 +600,7 @@ completion.
579
600
 
580
601
  Closing ceremony cost should scale with feature size. A feature whose
581
602
  **planned substantive** WU count (types `implementation`, `qa_authoring`,
582
- `qa_execution`, `qa_curation`) is **≤ 8** drafts as a **single gate** with
603
+ `qa_execution`, `qa_curation`) is **≤ 12** drafts as a **single gate** with
583
604
  a **single terminal `close` WU** — no `close-intermediate`, no `plan-next`.
584
605
  This is the proportional shape: small features do not pay multi-WU closing
585
606
  overhead sized for large ones.
@@ -597,6 +618,13 @@ reflective session. Ceremony proportionality trades reflection only on
597
618
  features that stay small **and** on-plan. The `gate_eval.py` predicate is
598
619
  the safety net; this rule does not replace it.
599
620
 
621
+ The line moved from 8 to 12 on 2026-09-26 (#3426): post-review features
622
+ average 10.7 substantive units, every extra gate costs a `plan-next` plus a
623
+ `close-intermediate` and an `awaiting_review` wait, and the arm predicate
624
+ would have armed only 18 of 74 gate boundaries it evaluated — the wait was a
625
+ human redraft, not a review. A feature goes multi-gate when a human decision
626
+ sits between its gates, not because it is large.
627
+
600
628
  **Reflective prose is conditional too (FEAT-2026-0106).** The roadmap once
601
629
  asked for a retrospective "only on `not_met` or on request" — superseded,
602
630
  for an ordering reason: the close writes `RETROSPECTIVE.md` before the judge
@@ -759,7 +787,17 @@ when and how these get created.
759
787
  | State backend | WU / GATE file frontmatter | GitHub issue labels + feature registry |
760
788
  | Dispatch | driver shells out (`claude -p`) | inbox files + polling loop |
761
789
  | Branch / merge | one branch, squash per WU | branch + PR per task, merge watcher |
762
- | Spec front-end | optional; task graph authored directly | spec-first (specs agent + codegen) |
790
+ | Spec front-end | one of: **none** (task graph authored directly), **consumed** (specs authored upstream, the loop implements against a handoff manifest), or **authored** (the spec tree is the deliverable — work units author spec files) | spec-first (specs agent + codegen), from a dossier **or** from an upstream handoff manifest |
791
+
792
+ The loop's spec front-end is three-valued because a repository whose entire
793
+ deliverable **is** the spec tree fits neither of the two values this row
794
+ carried before (#3407): it is not "optional", and the task graph is not
795
+ "authored directly" — the work units author spec files. One consumer carried
796
+ that third case in about forty lines of project `CLAUDE.md` because the row
797
+ could not express it. In the **authored** mode the work-unit contract picks up
798
+ two constraints that do not apply to code units — see
799
+ `authoring-work-units` §6 (the whole tree validates) and §12 (a structural
800
+ assertion substitutes for the red test).
763
801
 
764
802
  Everything above those rows — the unit hierarchy, ownership split, WU contract,
765
803
  verification-as-oracle, the gate cycle, plan-next, LEARNINGS, and autonomy — is
@@ -118,9 +118,22 @@
118
118
  # # attempt repairs the retained tree in place
119
119
  # # instead of re-authoring from scratch. Set to
120
120
  # # false to restore the pre-0.18 discard behaviour.
121
+ # dispatch_skills: false # whether a dispatched session loads the
122
+ # # project's skills index. Defaults to false:
123
+ # # a work unit invokes no skill, and the index
124
+ # # is fixed context paid on every turn (#3423).
125
+ # # Set true for a project whose units call
126
+ # # `/skill-name` in-session.
127
+ # post_merge_issue: false # whether a `met` terminal close files PLAN.md's
128
+ # # `## Post-merge checklist` as a tracked issue.
129
+ # # Defaults to false (#3424): the checklist is
130
+ # # appended under the feature's archived detail
131
+ # # section in roadmap-archive.md instead.
121
132
  # defaults:
122
133
  # max_attempts: 3
123
134
  # retain_on_guard_refusal: true
135
+ # dispatch_skills: false
136
+ # post_merge_issue: false
124
137
  #
125
138
  # The re-plan-after-two-failures behaviour (FEAT-2026-0104) reads this same
126
139
  # `max_attempts` resolution and adds no config key of its own — a unit's
@@ -1041,7 +1041,7 @@ def check_produces_satisfiability(feature_dir: Path, gates: list) -> None:
1041
1041
 
1042
1042
  # Ceremony-proportionality threshold (docs/methodology.md §6): one fact, one
1043
1043
  # home. Referenced, not redefined, in the draft-feature skill.
1044
- GATE_PROPORTIONALITY_THRESHOLD = 8
1044
+ GATE_PROPORTIONALITY_THRESHOLD = 12 # was 8 until #3426
1045
1045
  GATE_PROPORTIONALITY_SUBSTANTIVE_TYPES = frozenset(
1046
1046
  {"implementation", "qa_authoring", "qa_execution", "qa_curation"}
1047
1047
  )
@@ -42,6 +42,7 @@ from __future__ import annotations
42
42
  import argparse
43
43
  import datetime as dt
44
44
  import fnmatch
45
+ import functools
45
46
  import glob
46
47
  import hashlib
47
48
  import inspect
@@ -125,7 +126,7 @@ SPECFUSE_DIR = Path(".specfuse")
125
126
  REPO_ROOT = SPECFUSE_DIR.parent
126
127
  FEATURES_DIR = SPECFUSE_DIR / "features"
127
128
  VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
128
- DRIVER_VERSION = "0.23.3"
129
+ DRIVER_VERSION = "0.25.0"
129
130
  # Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
130
131
  # version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
131
132
  # the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
@@ -635,6 +636,66 @@ def resolve_retain_on_guard_refusal(verification_cfg: dict) -> bool:
635
636
  """
636
637
  project = (verification_cfg or {}).get("defaults") or {}
637
638
  return bool(project.get("retain_on_guard_refusal", True))
639
+
640
+
641
+ def resolve_dispatch_skills(verification_cfg: dict) -> bool:
642
+ """Whether a dispatched session gets the project's skills index (#3423).
643
+
644
+ Reads `defaults.dispatch_skills` from verification.yml — the same
645
+ `defaults` block `resolve_max_attempts` reads. Defaults to False: a
646
+ dispatched work unit invokes no skill (the narrow-tier instructions are in
647
+ the preamble and unit bodies name skill files by path, for reading), so
648
+ the index — 34 skills and 1,534 words of descriptions in this repository —
649
+ is fixed context carried in every turn's cache read for nothing. Set true
650
+ for a project whose units do invoke `/skill-name` in-session.
651
+ """
652
+ project = (verification_cfg or {}).get("defaults") or {}
653
+ return bool(project.get("dispatch_skills", False))
654
+
655
+
656
+ @functools.lru_cache(maxsize=None)
657
+ def claude_supports_flag(flag: str) -> bool:
658
+ """Whether the installed Claude Code CLI accepts *flag* (#3432).
659
+
660
+ Probed once per driver run from `claude --help`. A CLI that cannot be run
661
+ reads as not supporting anything: the dispatch then omits the flag and
662
+ fails on its own terms, not on an unknown option.
663
+ """
664
+ try:
665
+ proc = subprocess.run(
666
+ resolve_claude_cmd(["claude", "--help"]), capture_output=True,
667
+ text=True, check=False, timeout=30,
668
+ env=child_env_without_pin_marker(),
669
+ )
670
+ except Exception: # noqa: BLE001 - a missing or broken CLI is "no"
671
+ return False
672
+ out = "".join(s for s in (proc.stdout, proc.stderr) if isinstance(s, str))
673
+ return flag in out
674
+
675
+
676
+ _FLAG_WARNED: set[str] = set()
677
+
678
+
679
+ def _warn_flag_unsupported(flag: str, consequence: str) -> None:
680
+ if flag in _FLAG_WARNED:
681
+ return
682
+ _FLAG_WARNED.add(flag)
683
+ print(f" WARN: this Claude Code CLI does not accept {flag}; {consequence}")
684
+
685
+
686
+ def resolve_post_merge_issue(verification_cfg: dict) -> bool:
687
+ """Whether a `met` terminal close files the post-merge checklist as an issue (#3424).
688
+
689
+ Reads `defaults.post_merge_issue` from verification.yml. Defaults to False:
690
+ across this repository and its heaviest consumer, ten such issues were
691
+ filed and eight were never actioned — the checklist is a measurement note
692
+ for the operator, not a work item. The record survives instead under the
693
+ feature's archived detail section (`auto_archive_feature`).
694
+ """
695
+ project = (verification_cfg or {}).get("defaults") or {}
696
+ return bool(project.get("post_merge_issue", False))
697
+
698
+
638
699
  # Per-gate-command wall-clock ceiling. A gate that exceeds it is killed and the gate
639
700
  # FAILS (not hangs) — so a deadlocked command (e.g. a test blocked on input()) can't
640
701
  # stall the whole driver indefinitely. Generous vs real suites (this repo's is ~20s).
@@ -657,9 +718,16 @@ MODEL_BY_TYPE = {
657
718
  "retrospective": "sonnet",
658
719
  "lessons": "sonnet",
659
720
  "docs": "sonnet",
660
- "plan-next": "opus",
661
- "close": "opus",
662
- "close-intermediate": "opus",
721
+ # #3425: closing sessions on sonnet. Opus was 56% of all spend while
722
+ # implementation ran on sonnet; the judge (JUDGE_MODEL, itself sonnet,
723
+ # lower-only, ~$0.46 per close) is the independent check on a close's
724
+ # measurements and lowered 9 of 54 verdicts correctly, so the close does
725
+ # not need a bigger model than the session that checks it. A unit's own
726
+ # `model:` still overrides; `judged` events carry `close_model` so
727
+ # agreement can be split by model.
728
+ "plan-next": "sonnet",
729
+ "close": "sonnet",
730
+ "close-intermediate": "sonnet",
663
731
  }
664
732
  EFFORT_BY_TYPE = {
665
733
  "implementation": "medium",
@@ -4439,8 +4507,9 @@ def squash_commit(
4439
4507
  # --------------------------------------------------------------------------- #
4440
4508
 
4441
4509
  PROMPT_PREAMBLE = """\
4442
- You are executing a single Specfuse work unit. Read .specfuse/rules/ in full before \
4443
- acting; they are binding. Do NOT run any git command — the driver owns all commits \
4510
+ You are executing a single Specfuse work unit. The binding rules are already in \
4511
+ your context through .claude/CLAUDE.md; read a further file under .specfuse/rules/ \
4512
+ only when your unit body names it. Do NOT run any git command — the driver owns all commits \
4444
4513
  and bookkeeping. Edit files only. Verification is tiered: before you report, run \
4445
4514
  only the per-attempt (narrow) tier yourself — your unit type's gate set in \
4446
4515
  .specfuse/verification.yml minus every gate declaring `tier: broad`, with the \
@@ -4790,7 +4859,8 @@ def format_reverification_worklist(wu: WorkUnit, feature_dir: Path) -> str:
4790
4859
 
4791
4860
 
4792
4861
  def dispatch(wu: WorkUnit, failure_note: str | None,
4793
- cost_tracking: bool = True) -> tuple[str, dict | None]:
4862
+ cost_tracking: bool = True, *,
4863
+ dispatch_skills: bool | None = None) -> tuple[str, dict | None]:
4794
4864
  """Run a fresh agent session for this WU.
4795
4865
 
4796
4866
  When `cost_tracking` is True (default), requests JSON output from
@@ -4810,6 +4880,24 @@ def dispatch(wu: WorkUnit, failure_note: str | None,
4810
4880
  cmd = [p.replace("{model}", wu.model).replace("{effort}", wu.effort)
4811
4881
  for p in CLAUDE_CMD]
4812
4882
  cmd = resolve_claude_cmd(cmd)
4883
+ if dispatch_skills is None:
4884
+ # Resolved here rather than threaded through execute_unit_attempt:
4885
+ # every test fake of dispatch() takes the three positional arguments
4886
+ # and nothing else, and the yaml read is nothing next to a session.
4887
+ try:
4888
+ dispatch_skills = resolve_dispatch_skills(load_verification())
4889
+ except Exception: # noqa: BLE001 - an unreadable config keeps the default
4890
+ dispatch_skills = False
4891
+ if not dispatch_skills:
4892
+ # #3423: a dispatched unit invokes no skill, so the skills index is
4893
+ # fixed context paid on every turn. `defaults.dispatch_skills: true`
4894
+ # in verification.yml restores it (resolve_dispatch_skills).
4895
+ if claude_supports_flag("--disable-slash-commands"):
4896
+ cmd.insert(2, "--disable-slash-commands")
4897
+ else:
4898
+ _warn_flag_unsupported(
4899
+ "--disable-slash-commands",
4900
+ "the skills index stays in the session prompt (#3432)")
4813
4901
  if wu.unsandboxed:
4814
4902
  # Per-WU sandbox-escape. Audited via the unsandboxed_dispatch event
4815
4903
  # emitted in run()'s attempt loop; rationale lives in WU frontmatter.
@@ -7368,6 +7456,19 @@ def _reconcile_moved_section(
7368
7456
  return section_text
7369
7457
 
7370
7458
 
7459
+ def _post_merge_checklist_for_archive(feature_id: str, repo_root: Path) -> str | None:
7460
+ """PLAN.md's `## Post-merge checklist` body for *feature_id*, or None (#3424)."""
7461
+ features = repo_root / ".specfuse" / "features"
7462
+ for plan in sorted(features.glob(f"{feature_id}-*/PLAN.md")):
7463
+ try:
7464
+ _, body = read_frontmatter(plan)
7465
+ except Exception: # noqa: BLE001 - archive must never fail on a bad PLAN
7466
+ return None
7467
+ section = find_post_merge_checklist_section(body)
7468
+ return section.strip("\n") + "\n" if section else None
7469
+ return None
7470
+
7471
+
7371
7472
  def auto_archive_feature(feature_id: str, repo_root: Path) -> str:
7372
7473
  """Re-implement roadmap-archive single-feature algorithm (Steps 1–6) in-driver.
7373
7474
 
@@ -7480,6 +7581,13 @@ def auto_archive_feature(feature_id: str, repo_root: Path) -> str:
7480
7581
  f"](roadmap.md#{feat_id_lower})", f"](#{feat_id_lower})"
7481
7582
  )
7482
7583
 
7584
+ # #3424: the feature's post-merge checklist travels with the archived
7585
+ # detail section instead of becoming an issue nobody actions. Read from
7586
+ # the feature folder's PLAN.md when one exists; a row-only feature has none.
7587
+ checklist = _post_merge_checklist_for_archive(feature_id, repo_root)
7588
+ if checklist and "**Post-merge checklist.**" not in section_text:
7589
+ section_text = section_text.rstrip("\n") + "\n\n**Post-merge checklist.**\n\n" + checklist
7590
+
7483
7591
  marker_end = archive_text.index(marker) + len(marker)
7484
7592
  new_archive = archive_text[:marker_end] + f"\n{anchor}\n{section_text}" + archive_text[marker_end:]
7485
7593
  archive_path.write_text(new_archive)
@@ -8833,6 +8941,7 @@ def record_tracked_issue(
8833
8941
 
8834
8942
  def file_followup_issues(
8835
8943
  feature_dir: Path, repo_root: Path, runner=None, *, commit: bool | None = None,
8944
+ post_merge_issue: bool | None = None,
8836
8945
  ) -> dict:
8837
8946
  """File one tracked GitHub issue per FOLLOW-UPS.md entry, after a close's squash.
8838
8947
 
@@ -8883,7 +8992,13 @@ def file_followup_issues(
8883
8992
  followups_path,
8884
8993
  ))
8885
8994
 
8886
- if plan_fm.get("verdict") == "met" or plan_fm.get("status") == "done":
8995
+ if post_merge_issue is None:
8996
+ post_merge_issue = resolve_post_merge_issue(load_verification())
8997
+ if post_merge_issue and (
8998
+ plan_fm.get("verdict") == "met" or plan_fm.get("status") == "done"
8999
+ ):
9000
+ # #3424: off by default — the checklist lands under the archived
9001
+ # detail section instead (auto_archive_feature).
8887
9002
  section = find_post_merge_checklist_section(plan_body)
8888
9003
  if section:
8889
9004
  entries.append((
@@ -9488,6 +9603,15 @@ def capture_gate_diff(
9488
9603
  return "\n\n".join(parts)
9489
9604
 
9490
9605
 
9606
+ def _is_relative_to(path: Path, other: Path) -> bool:
9607
+ """`Path.is_relative_to`, tolerant of paths that do not resolve."""
9608
+ try:
9609
+ path.relative_to(other)
9610
+ except ValueError:
9611
+ return False
9612
+ return True
9613
+
9614
+
9491
9615
  def write_judge_followups(
9492
9616
  feature_dir: Path, gate_number: int, findings: list,
9493
9617
  ) -> Path:
@@ -9579,6 +9703,7 @@ def judge_close(
9579
9703
  payload: dict = {
9580
9704
  "gate": gate_number,
9581
9705
  "close_verdict": close_verdict,
9706
+ "close_model": getattr(wu, "model", None),
9582
9707
  "judge_verdict": None,
9583
9708
  "verdict": close_verdict,
9584
9709
  "lowered": False,
@@ -9644,6 +9769,32 @@ def judge_close(
9644
9769
  feature_dir, gate_number, result.findings)
9645
9770
  payload["lowered"] = True
9646
9771
  payload["verdict"] = "not_met"
9772
+ # Say WHERE the findings went (#3406). They are written verbatim to
9773
+ # FOLLOW-UPS.md above and committed below, but the event said only
9774
+ # `findings: N` with `reason: null` — so a reader of events.jsonl had
9775
+ # nothing pointing out of it. One operator searched events.jsonl,
9776
+ # `work/` and for a repository-level judge artifact, concluded the text
9777
+ # did not exist, and priced a third close at ~$12 to re-derive a
9778
+ # finding that was sitting in a committed file.
9779
+ #
9780
+ # The criteria go in; the finding TEXT deliberately does not. It is
9781
+ # already recorded verbatim in one place, and copying it into a second
9782
+ # invites the two to disagree — `write_judge_followups` exists so the
9783
+ # judge's words are not reformatted by the driver.
9784
+ payload["findings_path"] = str(
9785
+ Path(followups).relative_to(Path(feature_dir).parent.parent)
9786
+ if _is_relative_to(Path(followups), Path(feature_dir).parent.parent)
9787
+ else Path(followups)
9788
+ )
9789
+ payload["finding_criteria"] = [
9790
+ f.criterion for f in result.findings if getattr(f, "criterion", None)
9791
+ ]
9792
+ _named = ", ".join(payload["finding_criteria"]) or "no named criterion"
9793
+ payload["reason"] = (
9794
+ f"judge lowered the verdict on {payload['findings']} finding(s) "
9795
+ f"({_named}); the judge's own words are in "
9796
+ f"{payload['findings_path']}"
9797
+ )
9647
9798
  commit_bookkeeping(
9648
9799
  [wu.file, followups],
9649
9800
  f"chore(loop): {wu.wu_id} judge lowered verdict to not_met"
@@ -182,7 +182,10 @@ def _list_open_issues(runner: Callable, repo: str, *, limit: int) -> list:
182
182
  return []
183
183
 
184
184
 
185
- def apply_triage(runner: Callable, repo: str, decisions: list, *, auto: bool = False) -> list:
185
+ def apply_triage(
186
+ runner: Callable, repo: str, decisions: list, *,
187
+ auto: bool = False, label_projection: "dict | None" = None,
188
+ ) -> list:
186
189
  """Record each decision in `decisions` against its GitHub issue.
187
190
 
188
191
  Each decision is a mapping carrying at least `number`, `body` (the
@@ -195,6 +198,18 @@ def apply_triage(runner: Callable, repo: str, decisions: list, *, auto: bool = F
195
198
  marker. But a marker with no matching label on the issue (the label
196
199
  write failed on an earlier pass, e.g. because the label did not exist
197
200
  yet) is not fully idempotent -- it is missing its projection, so this
201
+ `label_projection` maps a canonical severity value to the label this
202
+ repository actually spells it with (`labels.severity_label_projection`,
203
+ #3386). Without it `severity_aliases` was one-directional: the bug lane
204
+ translated a repository's own words inbound while triage wrote canonical
205
+ words outbound, which that repository may never have defined. The write then
206
+ failed, and since the lane reads severity from the LABEL rather than the
207
+ marker, triage removed from the lane the very issues it had just classified
208
+ for it. The marker always records the canonical value, so it stays
209
+ comparable across repositories; only the label takes the operator's word.
210
+ Absent, or missing a value, the canonical label is written — the behaviour
211
+ before the projection existed.
212
+
198
213
  retries only the label write. `decision["labels"]` (as returned by
199
214
  `gh issue list --json ...,labels`, i.e. a list of `{"name": ...}`
200
215
  mappings) is what `labels` is checked against; a decision with no
@@ -226,7 +241,10 @@ def apply_triage(runner: Callable, repo: str, decisions: list, *, auto: bool = F
226
241
  marked_severity = (parse_marker_fields(body) or {}).get("severity")
227
242
  target_labels = labels_for(marked_category) if marked_category in CATEGORIES else ()
228
243
  if marked_severity:
229
- target_labels = tuple(target_labels) + (severity_label_for(marked_severity),)
244
+ target_labels = tuple(target_labels) + (
245
+ (label_projection or {}).get(
246
+ marked_severity, severity_label_for(marked_severity)),
247
+ )
230
248
  existing_labels = {
231
249
  label.get("name") for label in decision.get("labels") or []
232
250
  }
@@ -288,7 +306,9 @@ def apply_triage(runner: Callable, repo: str, decisions: list, *, auto: bool = F
288
306
 
289
307
  labels_to_add = list(labels_for(applied_category))
290
308
  if severity:
291
- labels_to_add.append(severity_label_for(severity))
309
+ labels_to_add.append(
310
+ (label_projection or {}).get(
311
+ severity, severity_label_for(severity)))
292
312
 
293
313
  try:
294
314
  runner(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.23.3
3
+ Version: 0.25.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