specfuse-loop 0.28.2__tar.gz → 0.29.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 (676) hide show
  1. {specfuse_loop-0.28.2/specfuse_loop.egg-info → specfuse_loop-0.29.0}/PKG-INFO +1 -1
  2. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/pyproject.toml +1 -1
  3. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/diagnose_invoke.py +6 -2
  4. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/findings_autofix.py +26 -2
  5. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/findings_diagnose.py +80 -15
  6. specfuse_loop-0.29.0/specfuse/loop/data/VERSION +1 -0
  7. specfuse_loop-0.29.0/specfuse/loop/data/docs/concepts/monitoring-runners.md +199 -0
  8. specfuse_loop-0.29.0/specfuse/loop/data/docs/concepts/monitoring-schema.md +211 -0
  9. specfuse_loop-0.29.0/specfuse/loop/data/docs/concepts/monitoring-to-fix.md +249 -0
  10. specfuse_loop-0.29.0/specfuse/loop/data/docs/flows.html +519 -0
  11. specfuse_loop-0.29.0/specfuse/loop/data/docs/lifecycles.md +394 -0
  12. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/gitignore.snippet +2 -0
  13. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/labels.py +8 -0
  14. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/loop.py +12 -3
  15. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/scaffold.py +13 -3
  16. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/artifact.py +11 -0
  17. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/cli.py +154 -3
  18. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/diagnose_cli.py +6 -0
  19. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/diagnosis.py +31 -1
  20. specfuse_loop-0.29.0/specfuse/monitor/issues.py +572 -0
  21. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/providers/azure_app_insights.py +377 -32
  22. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/providers/azure_service_bus.py +157 -1
  23. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/redaction.py +6 -0
  24. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
  25. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/SOURCES.txt +16 -0
  26. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_app_insights_query_schema_check.py +38 -2
  27. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_app_insights_workspace_schema.py +6 -2
  28. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_caller_check_ratchet.py +0 -1
  29. specfuse_loop-0.29.0/tests/test_diagnosis_severity_end_to_end.py +191 -0
  30. specfuse_loop-0.29.0/tests/test_diagnosis_severity_rubric.py +41 -0
  31. specfuse_loop-0.29.0/tests/test_flows_page.py +94 -0
  32. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_heartbeat_adapter.py +70 -4
  33. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_init_integration.py +5 -0
  34. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_label_registry.py +4 -2
  35. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitor_check_isolation.py +2 -0
  36. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitor_cli.py +10 -1
  37. specfuse_loop-0.29.0/tests/test_monitor_diagnose_auto_warning.py +120 -0
  38. specfuse_loop-0.29.0/tests/test_monitor_finding_context_end_to_end.py +463 -0
  39. specfuse_loop-0.29.0/tests/test_monitor_finding_lifecycle_end_to_end.py +268 -0
  40. specfuse_loop-0.29.0/tests/test_monitor_finding_meta.py +208 -0
  41. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitor_issue_lifecycle.py +17 -12
  42. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitor_one_issue_per_fingerprint.py +2 -0
  43. specfuse_loop-0.29.0/tests/test_monitor_quiet_annotation.py +189 -0
  44. specfuse_loop-0.29.0/tests/test_rearm_close_broad_run.py +124 -0
  45. specfuse_loop-0.29.0/tests/test_rediagnose_on_reopen.py +151 -0
  46. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_data_in_sync.py +5 -0
  47. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_docs.py +5 -0
  48. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_init.py +5 -0
  49. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_resources.py +5 -0
  50. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_wiring.py +7 -1
  51. specfuse_loop-0.29.0/tests/test_upgrade_gitignore_lines.py +64 -0
  52. specfuse_loop-0.28.2/specfuse/loop/data/VERSION +0 -1
  53. specfuse_loop-0.28.2/specfuse/monitor/issues.py +0 -303
  54. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/LICENSE +0 -0
  55. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/NOTICE +0 -0
  56. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/README.md +0 -0
  57. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/setup.cfg +0 -0
  58. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/__init__.py +0 -0
  59. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/budget.py +0 -0
  60. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/drafting_answers.py +0 -0
  61. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/drafting_invoke.py +0 -0
  62. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/drafting_questions.py +0 -0
  63. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/driver_command.py +0 -0
  64. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/driver_invoke.py +0 -0
  65. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/invoke.py +0 -0
  66. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/monitoring_read.py +0 -0
  67. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/__init__.py +0 -0
  68. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/answers.py +0 -0
  69. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/bugs.py +0 -0
  70. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/feature.py +0 -0
  71. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/providers/triage.py +0 -0
  72. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/queue_read.py +0 -0
  73. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/repo_detect.py +0 -0
  74. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/run.py +0 -0
  75. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/severity_backfill.py +0 -0
  76. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/state.py +0 -0
  77. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/triage_invoke.py +0 -0
  78. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/agent/worktree.py +0 -0
  79. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/__init__.py +0 -0
  80. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/_filelock.py +0 -0
  81. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/_miniyaml.py +0 -0
  82. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/_wu_sections.py +0 -0
  83. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/adopt_feature.py +0 -0
  84. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/agent_policy.py +0 -0
  85. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/arm_eval.py +0 -0
  86. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/arm_sweep.py +0 -0
  87. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/arm_txn.py +0 -0
  88. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/bug_lane.py +0 -0
  89. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/bug_lane_run.py +0 -0
  90. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/bug_lane_state.py +0 -0
  91. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/build_provenance.py +0 -0
  92. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/caller_check.py +0 -0
  93. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/changelog.py +0 -0
  94. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/closing_requirements.py +0 -0
  95. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/cost.py +0 -0
  96. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/criteria_state.py +0 -0
  97. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  98. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  99. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  100. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  101. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  102. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  103. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/getting-started.md +0 -0
  104. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/glossary.md +0 -0
  105. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/methodology.md +0 -0
  106. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/docs/skills.md +0 -0
  107. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  108. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  109. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/monitoring.yml.example +0 -0
  110. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/roadmap.template.md +0 -0
  111. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/close-discipline.md +0 -0
  112. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  113. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  114. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/human-output.md +0 -0
  115. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/never-touch.md +0 -0
  116. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  117. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  118. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/result-contract.md +0 -0
  119. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  120. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  121. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/rules-local/README.md +0 -0
  122. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  123. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/event.schema.json +0 -0
  124. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  125. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  126. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  127. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  128. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  129. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/GATE.template.md +0 -0
  130. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  131. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  132. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/templates/WU.template.md +0 -0
  133. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/verification.yml.example +0 -0
  134. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  135. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/decisions_format.py +0 -0
  136. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/driver_edit.py +0 -0
  137. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/escalation.py +0 -0
  138. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/events_stats.py +0 -0
  139. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/gate_commands.py +0 -0
  140. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/gate_eval.py +0 -0
  141. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/gh_backend.py +0 -0
  142. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/gh_features.py +0 -0
  143. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/heartbeat.py +0 -0
  144. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/judge.py +0 -0
  145. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/learnings_distil.py +0 -0
  146. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/learnings_query.py +0 -0
  147. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/lint_closing.py +0 -0
  148. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/lint_monitoring.py +0 -0
  149. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/lint_plan.py +0 -0
  150. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/lint_roadmap.py +0 -0
  151. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/notify.py +0 -0
  152. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/notify_escalation.py +0 -0
  153. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/notify_sla.py +0 -0
  154. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/plan_baseline.py +0 -0
  155. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/policy_proposals.py +0 -0
  156. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/policy_review.py +0 -0
  157. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/prerun.py +0 -0
  158. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/prerun_capture.py +0 -0
  159. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/promotion.py +0 -0
  160. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/rearm_migration.py +0 -0
  161. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/red_on_base.py +0 -0
  162. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/replay_spin.py +0 -0
  163. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/roadmap_archive.py +0 -0
  164. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/triage.py +0 -0
  165. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/upgrade_merge_gate.py +0 -0
  166. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/loop/validate_event.py +0 -0
  167. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/__init__.py +0 -0
  168. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/adapters.py +0 -0
  169. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/autofix.py +0 -0
  170. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/autofix_invoke.py +0 -0
  171. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/autofix_run.py +0 -0
  172. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/autofix_state.py +0 -0
  173. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/durations.py +0 -0
  174. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/fingerprint.py +0 -0
  175. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/providers/__init__.py +0 -0
  176. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
  177. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse/monitor/schedule.py +0 -0
  178. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  179. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
  180. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/requires.txt +0 -0
  181. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/specfuse_loop.egg-info/top_level.txt +0 -0
  182. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_abandon_feature_skill.py +0 -0
  183. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_abandoned_work_reported.py +0 -0
  184. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
  185. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_adopt_feature.py +0 -0
  186. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_budget.py +0 -0
  187. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_bug_selection_dedup.py +0 -0
  188. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_default_features_root.py +0 -0
  189. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_driver_command.py +0 -0
  190. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_driver_invoke.py +0 -0
  191. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_driver_restart.py +0 -0
  192. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_escalation_on_issue.py +0 -0
  193. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_escalation_run_state.py +0 -0
  194. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_findings_seam.py +0 -0
  195. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_invoke_timeout.py +0 -0
  196. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_invoke_usage.py +0 -0
  197. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_item_worktree.py +0 -0
  198. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_lock.py +0 -0
  199. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_policy_key_ownership.py +0 -0
  200. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_policy_queue.py +0 -0
  201. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_policy_schema.py +0 -0
  202. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_policy_triage_dial.py +0 -0
  203. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_answers.py +0 -0
  204. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_bugs.py +0 -0
  205. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_feature.py +0 -0
  206. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_findings_autofix.py +0 -0
  207. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
  208. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_isolation.py +0 -0
  209. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_provider_triage.py +0 -0
  210. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_queue_read.py +0 -0
  211. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_relays_driver_output.py +0 -0
  212. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_repo_detect.py +0 -0
  213. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_run.py +0 -0
  214. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_run_progress.py +0 -0
  215. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_seam.py +0 -0
  216. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_snapshot_refresh.py +0 -0
  217. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_agent_state.py +0 -0
  218. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_answer_escalation_skill.py +0 -0
  219. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_app_insights_adapters.py +0 -0
  220. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_app_insights_harvest_window.py +0 -0
  221. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_archive_ref_reconciliation.py +0 -0
  222. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_eval.py +0 -0
  223. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_eval_lint_class.py +0 -0
  224. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_eval_wiring.py +0 -0
  225. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  226. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_sweep.py +0 -0
  227. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_sweep_gate.py +0 -0
  228. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_txn.py +0 -0
  229. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_arm_wiring.py +0 -0
  230. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_artifact_redaction.py +0 -0
  231. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attempt_note_untracked_files.py +0 -0
  232. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attempt_outcome_contract.py +0 -0
  233. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attempt_outcome_emission.py +0 -0
  234. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attention_nonwriting_guard.py +0 -0
  235. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attention_silence_section.py +0 -0
  236. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attention_skill_structure.py +0 -0
  237. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_attribution_halt_claims.py +0 -0
  238. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autoclose_deferral_visibility.py +0 -0
  239. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autoclose_gate_note.py +0 -0
  240. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  241. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autofix_invoke.py +0 -0
  242. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autofix_live.py +0 -0
  243. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autofix_predicate.py +0 -0
  244. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autofix_run.py +0 -0
  245. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autofix_state.py +0 -0
  246. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autosync.py +0 -0
  247. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autosync_consent.py +0 -0
  248. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autosync_firstrun.py +0 -0
  249. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autosync_no_cwd_leak.py +0 -0
  250. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_autosync_plugin.py +0 -0
  251. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_azure_credential_resolution.py +0 -0
  252. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_backend.py +0 -0
  253. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_baseline_escalation_message.py +0 -0
  254. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_baseline_persistence.py +0 -0
  255. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_baseline_probe.py +0 -0
  256. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_baseline_provenance.py +0 -0
  257. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_baseline_tree_hash_key.py +0 -0
  258. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bash_routing.py +0 -0
  259. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bats_suites_gated.py +0 -0
  260. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_binary_verdict.py +0 -0
  261. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_binding_block_advisory.py +0 -0
  262. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_binding_block_allocation.py +0 -0
  263. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_binding_block_budget.py +0 -0
  264. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_blind_run_is_not_drained.py +0 -0
  265. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  266. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  267. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bookkeeping_no_verify.py +0 -0
  268. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  269. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_ci_pending.py +0 -0
  270. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_guardrails.py +0 -0
  271. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
  272. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_labels_registered.py +0 -0
  273. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_min_severity.py +0 -0
  274. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_no_checks_pending.py +0 -0
  275. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_policy_contract.py +0 -0
  276. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
  277. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  278. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_run.py +0 -0
  279. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_spend.py +0 -0
  280. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_state.py +0 -0
  281. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  282. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_superseded.py +0 -0
  283. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_bug_lane_test_paths.py +0 -0
  284. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_build_provenance.py +0 -0
  285. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_carried_green_invalidated_by_diff.py +0 -0
  286. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_carry_accounting_close_and_judge.py +0 -0
  287. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_changed_file_test_selection.py +0 -0
  288. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_changelog_append_only.py +0 -0
  289. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_changelog_collection.py +0 -0
  290. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_changelog_release_wiring.py +0 -0
  291. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_changelog_schema.py +0 -0
  292. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_ci_conclusion_wait.py +0 -0
  293. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_ci_runs_every_gate.py +0 -0
  294. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_claude_resolution.py +0 -0
  295. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_close_b_pending_learnings.py +0 -0
  296. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_close_records_feature_oracle.py +0 -0
  297. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_close_wu_self_reference.py +0 -0
  298. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_closing_deliverable_guard.py +0 -0
  299. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_closing_guard_contracts.py +0 -0
  300. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_closing_guard_prediction.py +0 -0
  301. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_closing_learnings_destination.py +0 -0
  302. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_closing_requirements.py +0 -0
  303. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_closing_sessions_on_sonnet.py +0 -0
  304. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
  305. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_conditional_reflection.py +0 -0
  306. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_console_encoding.py +0 -0
  307. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_convergent_iteration.py +0 -0
  308. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_convergent_wu_lint.py +0 -0
  309. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_correlation_id_override.py +0 -0
  310. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_correlation_id_two_surfaces.py +0 -0
  311. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_cost_lifetime.py +0 -0
  312. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_cost_tracking.py +0 -0
  313. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_criteria_per_gate_lint.py +0 -0
  314. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_criteria_state.py +0 -0
  315. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_criteria_worklist.py +0 -0
  316. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_decision_citation_lint.py +0 -0
  317. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_decision_followups_discharged.py +0 -0
  318. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_decision_override_lint.py +0 -0
  319. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_decisions_format.py +0 -0
  320. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_default_severity_aliases.py +0 -0
  321. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_degraded_oracle.py +0 -0
  322. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_deliverable_missing_note.py +0 -0
  323. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_deliverable_presence_gate.py +0 -0
  324. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
  325. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_derive_agent_policy_skill.py +0 -0
  326. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_derive_monitoring_discovery.py +0 -0
  327. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
  328. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_design_for_diagnosis_rule.py +0 -0
  329. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_deterministic_refusal_repeat.py +0 -0
  330. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_diagnose_fenced_analysis.py +0 -0
  331. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_diagnose_headless.py +0 -0
  332. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_diagnose_issue_skill.py +0 -0
  333. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_diagnosis.py +0 -0
  334. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
  335. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_dispatch_model_from_fix_scope.py +0 -0
  336. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
  337. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_dispatch_skeleton.py +0 -0
  338. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_dispatch_skills_index.py +0 -0
  339. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_distilled_accept_step.py +0 -0
  340. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_doctor.py +0 -0
  341. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_doctor_command_preflight.py +0 -0
  342. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_done_feature_gates.py +0 -0
  343. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_draft_feature_answers_mode.py +0 -0
  344. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_drafting_answer_gate.py +0 -0
  345. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_drafting_invoke.py +0 -0
  346. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_drafting_question_issue.py +0 -0
  347. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_drafting_questions.py +0 -0
  348. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_drafting_reply_shape.py +0 -0
  349. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_edit_detection.py +0 -0
  350. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  351. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_integration.py +0 -0
  352. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_lock.py +0 -0
  353. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_module_surface.py +0 -0
  354. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
  355. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_restart_halt_wiring.py +0 -0
  356. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_restart_hold.py +0 -0
  357. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_staleness_gate_summary.py +0 -0
  358. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_driver_staleness_warning.py +0 -0
  359. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
  360. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_duration_tracking.py +0 -0
  361. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_empty_files_escalation.py +0 -0
  362. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_ensure_feature_branch.py +0 -0
  363. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_ensure_feature_branch_base.py +0 -0
  364. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_escalation_assignee_policy.py +0 -0
  365. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_escalation_contract.py +0 -0
  366. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_escalation_crash_containment.py +0 -0
  367. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_escalation_dedup_search.py +0 -0
  368. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_escalation_emit.py +0 -0
  369. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_event_gate_full_envelope.py +0 -0
  370. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_events_redaction.py +0 -0
  371. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_events_stats.py +0 -0
  372. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_extra_gates.py +0 -0
  373. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_failure_artifact_model.py +0 -0
  374. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_failure_signature_fail_prefix.py +0 -0
  375. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_failure_signature_names_the_test.py +0 -0
  376. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
  377. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_branch_auto_sync.py +0 -0
  378. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_branch_squash_merge.py +0 -0
  379. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_branch_stale_base.py +0 -0
  380. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_id_resolution.py +0 -0
  381. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_oracle_e2e.py +0 -0
  382. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  383. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_feature_review_accumulation.py +0 -0
  384. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_filelock_portable.py +0 -0
  385. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_files_changed_auto_repair.py +0 -0
  386. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_find_pr_no_search_index.py +0 -0
  387. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fingerprint.py +0 -0
  388. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_bug_cwd_worktree.py +0 -0
  389. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_bug_diff_self_check.py +0 -0
  390. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_bug_headless.py +0 -0
  391. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_bug_reads_comments.py +0 -0
  392. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
  393. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_unit_insertion_e2e.py +0 -0
  394. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fix_unit_insertion_refused.py +0 -0
  395. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_fold_contract_documented.py +0 -0
  396. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_followups_artifact.py +0 -0
  397. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_force_full_close.py +0 -0
  398. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
  399. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_broad_run.py +0 -0
  400. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_budget.py +0 -0
  401. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_budget_post_dispatch.py +0 -0
  402. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_commands_needs.py +0 -0
  403. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_eval.py +0 -0
  404. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_eval_calibration.py +0 -0
  405. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  406. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_eval_repo_flag.py +0 -0
  407. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_eval_terminal_wiring.py +0 -0
  408. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gate_report_verdict.py +0 -0
  409. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gh_backend.py +0 -0
  410. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gh_features.py +0 -0
  411. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_git_env_isolation.py +0 -0
  412. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_gitleaks_pinning.py +0 -0
  413. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_groom_backlog_skill.py +0 -0
  414. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_guard_refusal_failure_class.py +0 -0
  415. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  416. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_guard_repair_e2e.py +0 -0
  417. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_guard_repair_prompt.py +0 -0
  418. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_hashed_denylist.py +0 -0
  419. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_hashed_denylist_ci.py +0 -0
  420. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_headless_no_background_tasks.py +0 -0
  421. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_heartbeat.py +0 -0
  422. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_heartbeat_emission_contract.py +0 -0
  423. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_http_status_regression_adapter.py +0 -0
  424. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_http_status_regression_end_to_end.py +0 -0
  425. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_human_output_rule.py +0 -0
  426. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_human_work_unit.py +0 -0
  427. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_hygiene_boundary_guidance.py +0 -0
  428. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_hyphenated_gate_name.py +0 -0
  429. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_ignored_cleanup_before_probe.py +0 -0
  430. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_installed_copy_driver_e2e.py +0 -0
  431. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_interpreter_normalization.py +0 -0
  432. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_close_path.py +0 -0
  433. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_cost.py +0 -0
  434. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_cost_fold_committed.py +0 -0
  435. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_module.py +0 -0
  436. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_path_registry.py +0 -0
  437. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_sees_dropped_produces.py +0 -0
  438. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judge_terminal_flip_state.py +0 -0
  439. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_judged_event_points_at_findings.py +0 -0
  440. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_label_description_length.py +0 -0
  441. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_label_provisioning_runner_contract.py +0 -0
  442. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_label_registry_covers_consumers.py +0 -0
  443. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_language_aware_narrow_selection.py +0 -0
  444. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lazy_baseline_e2e.py +0 -0
  445. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_leak_findings_redaction.py +0 -0
  446. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_leak_scan.py +0 -0
  447. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_leak_scan_content.py +0 -0
  448. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
  449. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_learnings_distil_cli.py +0 -0
  450. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_learnings_query.py +0 -0
  451. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_learnings_staging.py +0 -0
  452. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
  453. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_learnings_weights.py +0 -0
  454. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  455. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lifecycle_integration.py +0 -0
  456. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lifetime_accounting.py +0 -0
  457. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_ac_observable.py +0 -0
  458. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_bare_produces_path.py +0 -0
  459. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_boundary_consistency.py +0 -0
  460. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_boundary_extraction.py +0 -0
  461. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_close_intermediate.py +0 -0
  462. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  463. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_close_wu.py +0 -0
  464. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_closing.py +0 -0
  465. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_closing_criteria.py +0 -0
  466. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
  467. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_correlation_id.py +0 -0
  468. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  469. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  470. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_feature_oracle_declared.py +0 -0
  471. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_gate_proportionality.py +0 -0
  472. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_monitoring.py +0 -0
  473. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_oracle_env.py +0 -0
  474. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_plan_contract_fields.py +0 -0
  475. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_plan_errors.py +0 -0
  476. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_plan_next_draft.py +0 -0
  477. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_plan_relative_import.py +0 -0
  478. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
  479. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_planning_sections.py +0 -0
  480. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_produces_driver_helper.py +0 -0
  481. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_produces_satisfiability.py +0 -0
  482. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_roadmap.py +0 -0
  483. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_sections.py +0 -0
  484. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  485. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_lint_verification_reference.py +0 -0
  486. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_live_tests_opt_in.py +0 -0
  487. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_load_bearing_close_lint.py +0 -0
  488. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_attempt_evidence.py +0 -0
  489. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_auto_archive.py +0 -0
  490. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_caveman_preamble.py +0 -0
  491. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_close_intermediate.py +0 -0
  492. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_criteria_skeleton.py +0 -0
  493. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_criteria_survival.py +0 -0
  494. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_defaults_by_type.py +0 -0
  495. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_effort.py +0 -0
  496. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_failure_note_cap.py +0 -0
  497. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_files_changed_guard.py +0 -0
  498. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_gate_budget.py +0 -0
  499. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_gate_needs.py +0 -0
  500. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_model_alias.py +0 -0
  501. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_orchestration.py +0 -0
  502. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_post_pass_invariant.py +0 -0
  503. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_reset_preserving_events.py +0 -0
  504. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_smoke_runner.py +0 -0
  505. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_unsandboxed.py +0 -0
  506. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_worklist_injection.py +0 -0
  507. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_loop_zero_token_guard.py +0 -0
  508. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  509. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_marker_dual_shape.py +0 -0
  510. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_maven_failure_excerpt.py +0 -0
  511. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_max_attempts_configurable.py +0 -0
  512. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_max_open_prs.py +0 -0
  513. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_migrate_legacy.py +0 -0
  514. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_miniyaml_equivalence.py +0 -0
  515. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_miniyaml_negative.py +0 -0
  516. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitor_durations.py +0 -0
  517. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitor_runner_surfaces.py +0 -0
  518. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  519. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitoring_cron_dialect.py +0 -0
  520. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitoring_example.py +0 -0
  521. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitoring_fenced_blocks.py +0 -0
  522. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_monitoring_seed.py +0 -0
  523. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_narrow_fallback_warning.py +0 -0
  524. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_needs_decision_outcome.py +0 -0
  525. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_no_pytest_subprocess.py +0 -0
  526. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_notify.py +0 -0
  527. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_notify_escalation.py +0 -0
  528. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_notify_sla.py +0 -0
  529. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_operator_escalation_rule.py +0 -0
  530. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_oracle_set_declared.py +0 -0
  531. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_oracle_unrunnable_attribution.py +0 -0
  532. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_per_run_item_cap_disclosure.py +0 -0
  533. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pick_feature_skill.py +0 -0
  534. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pin_honesty_and_integrity.py +0 -0
  535. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pin_marker_not_inherited.py +0 -0
  536. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pin_marker_spawn_sites.py +0 -0
  537. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_plan_baseline.py +0 -0
  538. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_planned_cost_lint.py +0 -0
  539. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_planning_cost_floor.py +0 -0
  540. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_plugin_prose_cross_refs.py +0 -0
  541. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_plugin_qualified_dispatch.py +0 -0
  542. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_policy_budgets_applied.py +0 -0
  543. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_policy_proposals.py +0 -0
  544. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_policy_review.py +0 -0
  545. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pr_checks_json_field.py +0 -0
  546. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pr_reference_in_title.py +0 -0
  547. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  548. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_prep_halt_operator_surface.py +0 -0
  549. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_prepare_feature.py +0 -0
  550. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_prepare_scaffold_sync.py +0 -0
  551. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_prerun_capture.py +0 -0
  552. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_prerun_oracles.py +0 -0
  553. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_prerun_wiring.py +0 -0
  554. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produced_document_verdict_guard.py +0 -0
  555. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_amendment_e2e.py +0 -0
  556. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_bracket_literal.py +0 -0
  557. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_bracket_wildcard.py +0 -0
  558. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_field.py +0 -0
  559. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_in_diff.py +0 -0
  560. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_justification.py +0 -0
  561. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_recursive_glob.py +0 -0
  562. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_repair_note.py +0 -0
  563. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_semantics_unified.py +0 -0
  564. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_produces_shape.py +0 -0
  565. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_progress_forward_note.py +0 -0
  566. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_progress_lines_end_to_end.py +0 -0
  567. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_promoted_issue_not_redispatched.py +0 -0
  568. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_promotion_ledger.py +0 -0
  569. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_provider_escalation_traces.py +0 -0
  570. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_provision_labels.py +0 -0
  571. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_queue_stalled_adapter.py +0 -0
  572. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  573. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_rearm_fold_marker.py +0 -0
  574. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_rearm_migration.py +0 -0
  575. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
  576. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_red_baseline_never_reused.py +0 -0
  577. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_red_on_base_fetch_and_reason.py +0 -0
  578. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_red_on_base_guardrail.py +0 -0
  579. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_red_on_base_overlay.py +0 -0
  580. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_red_on_base_probe.py +0 -0
  581. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_red_on_base_real_runner.py +0 -0
  582. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_redaction_windows_home.py +0 -0
  583. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_reescalation_posts_comment.py +0 -0
  584. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_reflection_required_signals.py +0 -0
  585. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replan_end_to_end.py +0 -0
  586. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replan_event_emission.py +0 -0
  587. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replan_note_collision.py +0 -0
  588. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replan_trigger.py +0 -0
  589. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replan_turn_contract.py +0 -0
  590. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replay_spin.py +0 -0
  591. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_replay_spin_failing_sets.py +0 -0
  592. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_required_checks.py +0 -0
  593. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_resolve_base.py +0 -0
  594. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_restart_cap_counts_progress.py +0 -0
  595. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_result_block.py +0 -0
  596. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_result_block_audience.py +0 -0
  597. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_result_block_near_miss.py +0 -0
  598. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_result_block_scalar.py +0 -0
  599. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_resume_command_build.py +0 -0
  600. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  601. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_add_skill.py +0 -0
  602. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_cli.py +0 -0
  603. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_shim.py +0 -0
  604. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_skill.py +0 -0
  605. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  606. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_link_gate.py +0 -0
  607. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_roadmap_row_parser.py +0 -0
  608. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_ruff_verdict_lines.py +0 -0
  609. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_rule_import_convergence.py +0 -0
  610. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_run_level_failure_breaker.py +0 -0
  611. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_doc_hygiene.py +0 -0
  612. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_label_provisioning.py +0 -0
  613. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_manifest.py +0 -0
  614. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_seed_sanity.py +0 -0
  615. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_skew_halts.py +0 -0
  616. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_scaffold_upgrade.py +0 -0
  617. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_schedule_dialect.py +0 -0
  618. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_seed_documents_prerun_keys.py +0 -0
  619. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_service_bus_dlq_adapter.py +0 -0
  620. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_session_env_preflight.py +0 -0
  621. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_aliases.py +0 -0
  622. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_backfill_apply.py +0 -0
  623. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_backfill_bugs_only.py +0 -0
  624. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_backfill_end_to_end.py +0 -0
  625. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_backfill_limit.py +0 -0
  626. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_backfill_marker.py +0 -0
  627. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_backfill_run.py +0 -0
  628. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_label_projection.py +0 -0
  629. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_reconciliation.py +0 -0
  630. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_severity_rubric.py +0 -0
  631. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_shipped_templates_parse.py +0 -0
  632. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_skill_discovery_links.py +0 -0
  633. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_skills_vendored_in_sync.py +0 -0
  634. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_spec_work_unit_rules.py +0 -0
  635. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_spinning_rearm_gate.py +0 -0
  636. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_spinning_repeat_same_failing_set.py +0 -0
  637. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_spinout_brief_end_to_end.py +0 -0
  638. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_spinout_brief_replan_option.py +0 -0
  639. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_squash_commit_hook_crash.py +0 -0
  640. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_stop_rationale_captured.py +0 -0
  641. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_stranded_halt_cause.py +0 -0
  642. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_surefire_run_level_signature.py +0 -0
  643. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_surefire_verdict_lines.py +0 -0
  644. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_template_closing_shapes.py +0 -0
  645. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_terminal_auto_close_changelog.py +0 -0
  646. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_terminal_flip_ownership.py +0 -0
  647. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_terminal_flips.py +0 -0
  648. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_terminal_gate_message.py +0 -0
  649. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_tiered_verification_e2e.py +0 -0
  650. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_timeout_kill_portable.py +0 -0
  651. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage.py +0 -0
  652. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_apply.py +0 -0
  653. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_projects_severity_label.py +0 -0
  654. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_severity_classify.py +0 -0
  655. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_severity_end_to_end.py +0 -0
  656. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_severity_write.py +0 -0
  657. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_skill_contract.py +0 -0
  658. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_triage_skips_agent_escalations.py +0 -0
  659. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_tsc_verdict_lines.py +0 -0
  660. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_umbrella_version_resolution.py +0 -0
  661. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
  662. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_unterminated_frontmatter.py +0 -0
  663. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_untracked_feature_folder.py +0 -0
  664. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_untracked_file_absorption.py +0 -0
  665. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_upgrade_integration.py +0 -0
  666. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_upgrade_merge_gate.py +0 -0
  667. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_validate_event.py +0 -0
  668. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_validate_event_driver_types.py +0 -0
  669. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_verdict_coupling.py +0 -0
  670. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_verify_empty_gate_set.py +0 -0
  671. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_version_consistency.py +0 -0
  672. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_version_skew.py +0 -0
  673. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_windows_gate_exec.py +0 -0
  674. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_worktree_restore.py +0 -0
  675. {specfuse_loop-0.28.2 → specfuse_loop-0.29.0}/tests/test_wu_execution_metadata.py +0 -0
  676. {specfuse_loop-0.28.2 → specfuse_loop-0.29.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.28.2
3
+ Version: 0.29.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.28.2"
7
+ version = "0.29.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"
@@ -23,7 +23,7 @@ from __future__ import annotations
23
23
 
24
24
  from specfuse.agent.invoke import build_claude_argv
25
25
  from specfuse.monitor.diagnose_cli import AnalysisParseError, render_headless
26
- from specfuse.monitor.diagnosis import FIX_SCOPES
26
+ from specfuse.monitor.diagnosis import FIX_SCOPES, SEVERITY_RUBRIC
27
27
 
28
28
  __all__ = ("AnalysisParseError", "build_invocation", "read_result")
29
29
 
@@ -69,7 +69,11 @@ def build_invocation(
69
69
  "partial evidence gets a low number.\n"
70
70
  f"- fix_scope: exactly one of {', '.join(FIX_SCOPES)} -- do not "
71
71
  "invent or default this value; if none plainly fits, say so in "
72
- "candidate_fix and still pick the closest of the three.\n\n"
72
+ "candidate_fix and still pick the closest of the three.\n"
73
+ "- severity (optional): how serious the finding is, judged from its "
74
+ "evidence against this rubric; omit it if you cannot tell:\n"
75
+ + "".join(f" - {line}\n" for line in SEVERITY_RUBRIC)
76
+ + "\n"
73
77
  "Do not post anything and do not run `gh issue comment` yourself; "
74
78
  "the caller renders and posts the diagnosis this session produces."
75
79
  )
@@ -7,7 +7,8 @@ confidence threshold, `fix_scope` routing, fingerprint rate limit, and daily
7
7
  cap all stay in `specfuse.monitor.autofix`/`autofix_state`, read only
8
8
  through `run_autofix`.
9
9
 
10
- `advertise` returns one `kind="finding-autofix"` item per open snapshot
10
+ `advertise` returns, most severe diagnosis first (stable within a severity),
11
+ one `kind="finding-autofix"` item per open snapshot
11
12
  issue carrying `specfuse.monitor.issues.FINDING_LABEL` that already has a
12
13
  diagnosis comment -- the mirror of T10's filter, read the same way via
13
14
  `gh issue view N --json body,comments` and `specfuse.monitor.diagnosis.parse`.
@@ -63,6 +64,7 @@ from specfuse.monitor.issues import FINDING_LABEL
63
64
 
64
65
  _ITEM_ID_PREFIX = "finding-autofix-"
65
66
  _DEFAULT_MONITORING_CONFIG_PATH = ".specfuse/monitoring.yml"
67
+ _SEVERITY_ORDER = ("critical", "high", "medium", "low")
66
68
  _FAILING_OUTCOMES = ("refused", "could_not_proceed")
67
69
 
68
70
 
@@ -91,6 +93,24 @@ def _has_diagnosis(comments: list) -> bool:
91
93
  return False
92
94
 
93
95
 
96
+ def _latest_diagnosis(comments: list):
97
+ latest = None
98
+ for comment in comments or []:
99
+ body = comment.get("body", "") if isinstance(comment, dict) else ""
100
+ try:
101
+ diagnosis = parse(body)
102
+ except DiagnosisParseError:
103
+ continue
104
+ if diagnosis is not None:
105
+ latest = diagnosis
106
+ return latest
107
+
108
+
109
+ def _severity_rank(diagnosis) -> int:
110
+ severity = diagnosis.severity if diagnosis is not None else None
111
+ return _SEVERITY_ORDER.index(severity) if severity in _SEVERITY_ORDER else len(_SEVERITY_ORDER)
112
+
113
+
94
114
  def _known_components(monitoring_config: Any) -> set:
95
115
  components = monitoring_config.get("components") if isinstance(monitoring_config, dict) else None
96
116
  if not isinstance(components, list):
@@ -171,6 +191,7 @@ class FindingsAutofixProvider:
171
191
  known_components = _known_components(monitoring_config)
172
192
 
173
193
  items = []
194
+ ranks = {}
174
195
  for issue in snapshot.issues:
175
196
  if FINDING_LABEL not in issue.labels:
176
197
  continue
@@ -183,7 +204,8 @@ class FindingsAutofixProvider:
183
204
  if component is None or component not in known_components:
184
205
  continue
185
206
 
186
- if not _has_diagnosis(comments):
207
+ diagnosis = _latest_diagnosis(comments)
208
+ if diagnosis is None:
187
209
  continue
188
210
 
189
211
  item_id = f"{_ITEM_ID_PREFIX}{issue.number}"
@@ -192,6 +214,7 @@ class FindingsAutofixProvider:
192
214
  "component": component,
193
215
  "monitoring_config": monitoring_config,
194
216
  }
217
+ ranks[item_id] = _severity_rank(diagnosis)
195
218
  items.append(
196
219
  ActionItem(
197
220
  item_id=item_id,
@@ -200,6 +223,7 @@ class FindingsAutofixProvider:
200
223
  queue_key=None,
201
224
  )
202
225
  )
226
+ items.sort(key=lambda item: ranks[item.item_id])
203
227
  return items
204
228
 
205
229
  def execute(self, item: ActionItem) -> ActionOutcome:
@@ -7,8 +7,9 @@
7
7
  `AgentSnapshot` cannot answer.
8
8
 
9
9
  `advertise` returns one `kind="finding-diagnose"` item per open snapshot
10
- issue carrying `specfuse.monitor.issues.FINDING_LABEL` that has no diagnosis
11
- comment yet. The snapshot's `IssueSummary` carries no body, so this
10
+ issue carrying `specfuse.monitor.issues.FINDING_LABEL` that needs a diagnosis:
11
+ none yet, a monitor reopen comment newer than its latest diagnosis, or the
12
+ `rediagnose` label (#3487). The snapshot's `IssueSummary` carries no body, so this
12
13
  provider reads each candidate's body and comments itself with
13
14
  `gh issue view N --json body,comments` -- the same read
14
15
  `autofix_run._read_finding_issue` performs and the same softening of "the
@@ -25,8 +26,8 @@ The rendered comment body is `diagnosis.render`'s, unaltered -- this module
25
26
  holds no heading template and no marker string of its own. An unparseable
26
27
  analysis result posts nothing: `execute` escalates with the parse error's
27
28
  own message and never retries, defaults a field, or posts a partial body.
28
- This provider performs no git mutation and no label write of its own; a
29
- diagnosis is a comment.
29
+ This provider performs no git mutation. Its only label write is the
30
+ `severity:<value>` label of a diagnosis that carries a severity.
30
31
  """
31
32
 
32
33
  from __future__ import annotations
@@ -51,14 +52,16 @@ from specfuse.agent.run import (
51
52
  _default_runner,
52
53
  )
53
54
  from specfuse.agent.state import AgentSnapshot
55
+ from specfuse.loop.agent_policy import SEVERITY_LABEL_PREFIX
54
56
  from specfuse.monitor.diagnosis import DiagnosisParseError, parse
55
- from specfuse.monitor.issues import FINDING_LABEL
57
+ from specfuse.monitor.issues import FINDING_LABEL, REDIAGNOSE_LABEL, is_reopen_comment
56
58
 
57
59
  _ITEM_ID_PREFIX = "finding-diagnose-"
58
60
  _DEFAULT_MONITORING_CONFIG_PATH = ".specfuse/monitoring.yml"
59
61
  _DIAGNOSE_DIAL_AUTO = "auto"
60
62
 
61
63
 
64
+
62
65
  def _read_issue(runner: Callable, repo: str, issue_number: int) -> dict:
63
66
  result = runner(
64
67
  ["gh", "issue", "view", str(issue_number), "--repo", repo, "--json", "body,comments"],
@@ -73,15 +76,25 @@ def _read_issue(runner: Callable, repo: str, issue_number: int) -> dict:
73
76
  return parsed if isinstance(parsed, dict) else {}
74
77
 
75
78
 
76
- def _already_diagnosed(comments: list) -> bool:
77
- for comment in comments or []:
79
+ def _is_diagnosis(body: str) -> bool:
80
+ try:
81
+ return parse(body) is not None
82
+ except DiagnosisParseError:
83
+ return False
84
+
85
+
86
+ def _needs_diagnosis(comments: list) -> bool:
87
+ """True when the finding has no diagnosis yet, or the monitor reopened it
88
+ after its latest diagnosis (#3487: a regression after a fix is diagnosed
89
+ again, once per reopen). `gh issue view` lists comments oldest first."""
90
+ last_diagnosis = last_reopen = -1
91
+ for index, comment in enumerate(comments or []):
78
92
  body = comment.get("body", "") if isinstance(comment, dict) else ""
79
- try:
80
- if parse(body) is not None:
81
- return True
82
- except DiagnosisParseError:
83
- continue
84
- return False
93
+ if _is_diagnosis(body):
94
+ last_diagnosis = index
95
+ elif is_reopen_comment(body):
96
+ last_reopen = index
97
+ return last_diagnosis < 0 or last_reopen > last_diagnosis
85
98
 
86
99
 
87
100
  def _unparseable_diagnosis_payload(number: int, error: str) -> EscalationPayload:
@@ -168,11 +181,15 @@ class FindingsDiagnoseProvider:
168
181
  if dial != _DIAGNOSE_DIAL_AUTO:
169
182
  continue
170
183
 
171
- if _already_diagnosed(comments):
184
+ relabelled = REDIAGNOSE_LABEL in issue.labels
185
+ if not relabelled and not _needs_diagnosis(comments):
172
186
  continue
173
187
 
174
188
  item_id = f"{_ITEM_ID_PREFIX}{issue.number}"
175
- self._rows[item_id] = {"number": issue.number, "title": issue.title, "body": body}
189
+ self._rows[item_id] = {
190
+ "number": issue.number, "title": issue.title, "body": body,
191
+ "relabelled": relabelled, "labels": tuple(issue.labels),
192
+ }
176
193
  items.append(
177
194
  ActionItem(
178
195
  item_id=item_id,
@@ -221,11 +238,59 @@ class FindingsDiagnoseProvider:
221
238
  ],
222
239
  check=False,
223
240
  )
241
+ self._label_severity(number, rendered, row.get("labels", ()))
242
+ if row.get("relabelled"):
243
+ # Consume the request so the next run does not diagnose again.
244
+ self._runner(
245
+ ["gh", "issue", "edit", str(number), "--repo", self._repo,
246
+ "--remove-label", REDIAGNOSE_LABEL],
247
+ check=False,
248
+ )
224
249
  return ActionOutcome(
225
250
  status=STATUS_COMPLETED,
226
251
  detail=f"issue #{number} diagnosed",
227
252
  spend=spend,
228
253
  )
229
254
 
255
+ def _read_labels(self, number: int) -> set:
256
+ result = self._runner(
257
+ ["gh", "issue", "view", str(number), "--repo", self._repo, "--json", "labels"],
258
+ check=False,
259
+ )
260
+ if getattr(result, "returncode", 1) != 0 or not getattr(result, "stdout", None):
261
+ return set()
262
+ try:
263
+ parsed = json.loads(result.stdout)
264
+ except ValueError:
265
+ return set()
266
+ entries = parsed.get("labels") if isinstance(parsed, dict) else None
267
+ return {
268
+ e["name"] for e in entries or []
269
+ if isinstance(e, dict) and isinstance(e.get("name"), str)
270
+ }
271
+
272
+ def _label_severity(self, number: int, rendered: str, labels) -> None:
273
+ """Give the finding exactly one `severity:<value>` label, replacing any
274
+ previous one. No severity on the diagnosis, no label write."""
275
+ diagnosis = parse(rendered)
276
+ severity = diagnosis.severity if diagnosis is not None else None
277
+ if severity is None:
278
+ return
279
+ wanted = f"{SEVERITY_LABEL_PREFIX}{severity}"
280
+ self._runner(
281
+ ["gh", "label", "create", wanted, "--force", "--repo", self._repo],
282
+ check=False,
283
+ )
284
+ current = set(labels) | self._read_labels(number)
285
+ stale = [
286
+ label for label in sorted(current)
287
+ if label.startswith(SEVERITY_LABEL_PREFIX) and label != wanted
288
+ ]
289
+ argv = ["gh", "issue", "edit", str(number), "--repo", self._repo]
290
+ for label in stale:
291
+ argv += ["--remove-label", label]
292
+ argv += ["--add-label", wanted]
293
+ self._runner(argv, check=False)
294
+
230
295
  def reconcile(self, item: ActionItem, outcome: ActionOutcome) -> None:
231
296
  return None
@@ -0,0 +1 @@
1
+ 0.29.0
@@ -0,0 +1,199 @@
1
+ # Where the monitoring cycle runs — the `runner` dial and its surfaces
2
+
3
+ `.specfuse/monitoring.yml` gives every component a `runner` dial: `local`,
4
+ `gh-actions`, or `in-cluster`. The dial says *where that component's
5
+ diagnose/autofix cycle executes* — it is a routing decision, not a feature
6
+ flag, and it does not gate any code path inside the cycle itself. This
7
+ document covers the two surfaces FEAT-2026-0040 ships and states plainly
8
+ what is still missing.
9
+
10
+ What happens to a finding after it is filed — diagnosis, the autofix decision,
11
+ the fix — and which processes must be scheduled for it is in
12
+ [`monitoring-to-fix.md`](monitoring-to-fix.md).
13
+ `specfuse monitor run` does not diagnose anything itself: when it files or
14
+ updates findings for a component set to `diagnose: auto`, its summary prints one
15
+ line saying those findings wait for `specfuse agent run` (specfuse-agent).
16
+
17
+ ## The two shipped surfaces
18
+
19
+ **Local.** An operator, or a cron entry on a machine they control, runs
20
+ `specfuse monitor run`. This is the recommended starting point — the
21
+ schema's own `monitoring.yml.example` advises starting every component at
22
+ `runner: local` before loosening any dial. Nothing extra to install — the
23
+ `specfuse` suite already carries it (`pipx install specfuse`, which
24
+ hard-depends on the `specfuse-loop` distribution this CLI ships in). The CLI
25
+ reads `.specfuse/monitoring.yml` from the current working directory the same
26
+ way on this surface as any other.
27
+
28
+ **GitHub Actions.** A shipped, scheduled workflow template runs the same
29
+ cycle in CI. It lives at
30
+ `specfuse/loop/data/workflows/specfuse-monitor.yml` in the `specfuse-loop`
31
+ distribution — this is a *template*, not a workflow this repository runs
32
+ itself (this repository is a CLI tool with no deployable components and
33
+ will never carry a real `monitoring.yml`; see `verification.yml`). To use
34
+ it in a consumer project:
35
+
36
+ 1. Copy `specfuse-monitor.yml` into that project's own `.github/workflows/`.
37
+ You already have it if `specfuse` is installed — it ships inside the
38
+ `specfuse_loop` package data, so
39
+ `python -c "import specfuse.loop, pathlib; print(pathlib.Path(specfuse.loop.__file__).parent / 'data/workflows/specfuse-monitor.yml')"`
40
+ prints its path. It is also readable from this repository's
41
+ `specfuse/loop/data/workflows/`.
42
+ 2. Add the secrets it references — one per credential-bearing environment
43
+ variable named in that project's `.specfuse/monitoring.yml` (`api_key`,
44
+ `connection_string`, etc.), plus a token with permission to file issues
45
+ (`GITHUB_TOKEN` is sufficient for issues in the same repository) — under
46
+ the consumer repository's Settings → Secrets and variables → Actions.
47
+ Every value the template consumes is a `${{ secrets.* }}` reference; it
48
+ never embeds a literal credential.
49
+ 3. Dial the components that should run here to `runner: gh-actions` in that
50
+ project's `.specfuse/monitoring.yml`.
51
+
52
+ The template declares both `on.schedule` (a cron expression) and
53
+ `on.workflow_dispatch`, so it can be triggered by hand while debugging, not
54
+ only on its schedule. It requests exactly `permissions: {issues: write,
55
+ contents: read}` — least privilege for filing issues, nothing more — rather
56
+ than inheriting the default write-all token.
57
+
58
+ ## The dial routes, and skips are reported, not silent
59
+
60
+ `specfuse monitor run` takes a `--runner` flag naming which surface this
61
+ invocation *is* (defaulting to `local`). It enumerates only the components
62
+ whose `runner` matches that flag; every other component is named in the run
63
+ summary along with the surface it belongs to, so a skipped component is
64
+ visible, not silently unmonitored. A component whose `runner` value isn't
65
+ one of `local` / `gh-actions` / `in-cluster` is a clear error naming the bad
66
+ value and the supported set — a typo'd dial must fail loudly, not run
67
+ nothing.
68
+
69
+ ## `in-cluster` is out of scope here
70
+
71
+ `in-cluster` is a valid schema value today, but no runner surface
72
+ implements it in this feature. A component dialed to `runner: in-cluster`
73
+ is reported by `specfuse monitor run` as unhandled by design — naming
74
+ **FEAT-2026-0043**, which owns that surface — and is neither dropped
75
+ silently nor treated as an error. Do not dial a component to `in-cluster`
76
+ expecting it to be monitored until that feature ships.
77
+
78
+ ## The harvest window
79
+
80
+ Each run reads only what is new since the previous one. The window for an
81
+ environment starts at its watermark (the time the previous non-dry run
82
+ started, kept in `.specfuse/monitor-watermarks/<env>.json`) and ends at this
83
+ run's start. With no readable watermark (a first run, a fresh CI runner, a new
84
+ operator) it starts 24 hours back instead, and the run summary says so.
85
+
86
+ The window applies to the checks that read event history: `error-logs` and
87
+ `http-5xx` filter on `ingestion_time()`, so a row stamped just before the
88
+ previous run but ingested after it is read by the next run rather than lost
89
+ between the two. Three check types are deliberately not bounded by it:
90
+
91
+ - `heartbeat` needs the most recent heartbeat however old it is;
92
+ - `http-status-regression` compares the windows around each deployment and
93
+ carries its own fixed lookback;
94
+ - `invariant` runs the operator's query as written. A query that reads event
95
+ history should bound itself (for example `| where TimeGenerated > ago(1h)`);
96
+ otherwise every matching row in retention is a finding, deduplicated only by
97
+ `fingerprint_by`.
98
+
99
+ A run in which any check errored leaves the watermark where it was, so the
100
+ next run retries the same window.
101
+
102
+ The watermark directory is local runtime state, not something to commit.
103
+ `specfuse init` adds `.specfuse/monitor-watermarks/` to a new project's
104
+ `.gitignore`, and `specfuse upgrade` adds it to a project initialised earlier
105
+ (it appends any snippet line the file lacks, leaving your own lines alone). On a CI
106
+ runner the directory starts empty every run — the shipped workflow template
107
+ does not cache it — so each run there reads the 24-hour fallback window.
108
+
109
+ ## What `--dry-run` does and does not gate
110
+
111
+ `--dry-run` performs the read-only `fetch_failures()` calls against real
112
+ telemetry/broker transports — it is not an offline mode. It gates only the
113
+ writes: the watermark file and every `gh` invocation. If you point a dry
114
+ run at a production telemetry backend, it will consume that backend's read
115
+ quota exactly as a real run would.
116
+
117
+ ## What a finding is, and how its issue is kept
118
+
119
+ Every finding is one GitHub issue per fingerprint. The fingerprint is the
120
+ `specfuse:finding` marker in the issue body, and it is the only thing that
121
+ identifies a finding: nothing a finding *carries* (its title, its context, the
122
+ time it was seen, a dead-lettered message's id) enters it.
123
+
124
+ ### A recurrence reopens; it never refiles, and nothing is ever closed
125
+
126
+ On each run, a fingerprint is matched against issues in three states:
127
+
128
+ - **Open** — the issue is updated, as before.
129
+ - **Closed within the last 90 days** — the issue is **reopened** with exactly one
130
+ comment carrying the new occurrence count and the first and last time it was
131
+ seen, in UTC. No second issue is filed, so the earlier diagnosis and any
132
+ "reopen if…" note stay attached to the finding.
133
+ - **Absent, or closed longer ago than 90 days** — a new issue is filed. After 90
134
+ quiet days that is the honest reading, and the bound keeps the listing under
135
+ GitHub's 100-row limit.
136
+
137
+ The monitor **reopens, never closes.** Closing on silence would erase the evidence
138
+ of an intermittent fault, so closing stays a human decision, and no code in
139
+ `specfuse/monitor` issues a close. A finding-meta marker the monitor cannot fully
140
+ understand (a hand-edited or truncated one) reads as absent rather than failing
141
+ the run.
142
+
143
+ ### A finding that goes quiet is annotated, once
144
+
145
+ An open finding whose fingerprint has not been seen for **7 days** gets one
146
+ comment naming it a candidate for close, and a marker in its body so the comment
147
+ is not repeated. The monitor only does this for a component every one of whose
148
+ checks ran without error in that cycle — a broken check never makes a finding
149
+ look quiet — and it still closes nothing. If the fingerprint fires again, the
150
+ issue's body is rewritten without the marker, so a later quiet period is
151
+ annotated again. The pass reuses the cycle's own issue listing; `--dry-run`
152
+ prints the issues it would annotate.
153
+
154
+ ### `dlq`: closing an issue means "acknowledged"
155
+
156
+ A dead-lettered message stays in the queue until someone resubmits or purges it,
157
+ so it is seen on every run. The `dlq` finding records the message ids it has seen.
158
+ A closed `dlq` issue whose recorded ids cover every message currently in the queue
159
+ **stays closed**, with no write at all — closing it means "acknowledged, replay
160
+ pending". A message id the issue has not recorded **reopens** it, with a comment
161
+ naming that message. Messages are only ever peeked; the monitor never receives,
162
+ completes or abandons one.
163
+
164
+ ### `heartbeat`: a schedule that has not emitted yet is not silent
165
+
166
+ A target whose latest expected firing precedes the component's earliest observed
167
+ heartbeat is *pending its first firing*, and produces no `silent-schedule`
168
+ finding. A target that has emitted before and then missed a firing still does, and
169
+ a component with no heartbeat at all files nothing.
170
+
171
+ ### What a finding contains
172
+
173
+ The title reads `<component>: <failure class> in <where>`, with no fingerprint.
174
+ Each issue carries the labels `component:<name>` and `check:<type>` (created on
175
+ first use). Every body states the occurrence count, when it was first seen (the earliest
176
+ occurrence the run's rows show; for `dlq`, the message's enqueued time) and when
177
+ the issue was last updated (UTC), what was seen in this run (count, first, last, and the instances), and the
178
+ **Observed** line. Per check, the sections after **Observed** are:
179
+
180
+ | check | sections |
181
+ | --- | --- |
182
+ | `error-logs` | Where (top in-app stack frame), Request (route and result code), Logger (category and message template), Likely client abort, Samples, Reproduce |
183
+ | `http-5xx` | Exception (type, message), Where, Dependency (target, type, result code, duration), Duration, Samples, Reproduce |
184
+ | `dlq` | Message (id, sequence number, subject, enqueued time, delivery count), Dead-letter reason, or the Exception message and top stack frames when the reason is blank, Body shape (the top-level keys, never the values), Peek it |
185
+ | `heartbeat` | Schedule (cron, dialect, timezone, expected firing), Last run (the function's last execution and result), Host last seen, a Host silent line when the component sent no request telemetry across the expected firing, and Earliest heartbeat (the component's first heartbeat, which the grace rule measures from) |
186
+ | `queue-stalled` | Queue (oldest active message age, depth, `stall_after`), Where |
187
+
188
+ A section with nothing to say is left out rather than printed empty. A
189
+ `queue-stalled` finding cannot show when the subscription last completed a
190
+ message: Service Bus runtime properties carry no completion time (their
191
+ `accessed_at_utc` changes on sends as well as receives, so it would mislead),
192
+ and reading one from the messages themselves would mean receiving them, which
193
+ the check never does.
194
+
195
+ Each check still issues exactly one query. Every correlation (an exception to its
196
+ request, a request to its dependency, a schedule to its host's liveness) is a join
197
+ inside that query. The **Reproduce** and **Peek it** sections are text for a
198
+ human to run, built from configuration and sample ids, and the monitor never runs
199
+ them. Every section is redacted before it leaves the machine.