specfuse-loop 0.28.0__tar.gz → 0.28.2__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 (659) hide show
  1. {specfuse_loop-0.28.0/specfuse_loop.egg-info → specfuse_loop-0.28.2}/PKG-INFO +1 -1
  2. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/pyproject.toml +1 -1
  3. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/severity_backfill.py +0 -3
  4. specfuse_loop-0.28.2/specfuse/loop/data/VERSION +1 -0
  5. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/loop.py +1 -1
  6. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/cli.py +66 -13
  7. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/diagnose_cli.py +15 -1
  8. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/providers/azure_app_insights.py +38 -4
  9. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2/specfuse_loop.egg-info}/PKG-INFO +1 -1
  10. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse_loop.egg-info/SOURCES.txt +4 -0
  11. specfuse_loop-0.28.2/tests/test_app_insights_harvest_window.py +132 -0
  12. specfuse_loop-0.28.2/tests/test_diagnose_fenced_analysis.py +69 -0
  13. specfuse_loop-0.28.2/tests/test_monitor_check_isolation.py +178 -0
  14. specfuse_loop-0.28.2/tests/test_monitor_one_issue_per_fingerprint.py +108 -0
  15. specfuse_loop-0.28.0/specfuse/loop/data/VERSION +0 -1
  16. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/LICENSE +0 -0
  17. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/NOTICE +0 -0
  18. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/README.md +0 -0
  19. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/setup.cfg +0 -0
  20. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/__init__.py +0 -0
  21. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/budget.py +0 -0
  22. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/diagnose_invoke.py +0 -0
  23. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/drafting_answers.py +0 -0
  24. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/drafting_invoke.py +0 -0
  25. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/drafting_questions.py +0 -0
  26. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/driver_command.py +0 -0
  27. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/driver_invoke.py +0 -0
  28. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/invoke.py +0 -0
  29. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/monitoring_read.py +0 -0
  30. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/__init__.py +0 -0
  31. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/answers.py +0 -0
  32. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/bugs.py +0 -0
  33. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/feature.py +0 -0
  34. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/findings_autofix.py +0 -0
  35. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/findings_diagnose.py +0 -0
  36. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/providers/triage.py +0 -0
  37. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/queue_read.py +0 -0
  38. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/repo_detect.py +0 -0
  39. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/run.py +0 -0
  40. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/state.py +0 -0
  41. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/triage_invoke.py +0 -0
  42. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/agent/worktree.py +0 -0
  43. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/__init__.py +0 -0
  44. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/_filelock.py +0 -0
  45. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/_miniyaml.py +0 -0
  46. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/_wu_sections.py +0 -0
  47. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/adopt_feature.py +0 -0
  48. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/agent_policy.py +0 -0
  49. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/arm_eval.py +0 -0
  50. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/arm_sweep.py +0 -0
  51. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/arm_txn.py +0 -0
  52. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/bug_lane.py +0 -0
  53. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/bug_lane_run.py +0 -0
  54. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/bug_lane_state.py +0 -0
  55. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/build_provenance.py +0 -0
  56. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/caller_check.py +0 -0
  57. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/changelog.py +0 -0
  58. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/closing_requirements.py +0 -0
  59. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/cost.py +0 -0
  60. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/criteria_state.py +0 -0
  61. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  62. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  63. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  64. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  65. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  66. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  67. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/getting-started.md +0 -0
  68. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/glossary.md +0 -0
  69. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/methodology.md +0 -0
  70. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/docs/skills.md +0 -0
  71. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/gitignore.snippet +0 -0
  72. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  73. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  74. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/monitoring.yml.example +0 -0
  75. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/roadmap.template.md +0 -0
  76. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/close-discipline.md +0 -0
  77. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  78. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  79. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/human-output.md +0 -0
  80. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/never-touch.md +0 -0
  81. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  82. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  83. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/result-contract.md +0 -0
  84. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  85. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  86. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/rules-local/README.md +0 -0
  87. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  88. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/schemas/event.schema.json +0 -0
  89. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  90. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  91. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  92. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  93. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  94. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/templates/GATE.template.md +0 -0
  95. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  96. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  97. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/templates/WU.template.md +0 -0
  98. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/verification.yml.example +0 -0
  99. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  100. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/decisions_format.py +0 -0
  101. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/driver_edit.py +0 -0
  102. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/escalation.py +0 -0
  103. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/events_stats.py +0 -0
  104. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/gate_commands.py +0 -0
  105. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/gate_eval.py +0 -0
  106. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/gh_backend.py +0 -0
  107. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/gh_features.py +0 -0
  108. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/heartbeat.py +0 -0
  109. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/judge.py +0 -0
  110. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/labels.py +0 -0
  111. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/learnings_distil.py +0 -0
  112. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/learnings_query.py +0 -0
  113. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/lint_closing.py +0 -0
  114. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/lint_monitoring.py +0 -0
  115. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/lint_plan.py +0 -0
  116. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/lint_roadmap.py +0 -0
  117. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/notify.py +0 -0
  118. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/notify_escalation.py +0 -0
  119. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/notify_sla.py +0 -0
  120. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/plan_baseline.py +0 -0
  121. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/policy_proposals.py +0 -0
  122. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/policy_review.py +0 -0
  123. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/prerun.py +0 -0
  124. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/prerun_capture.py +0 -0
  125. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/promotion.py +0 -0
  126. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/rearm_migration.py +0 -0
  127. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/red_on_base.py +0 -0
  128. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/replay_spin.py +0 -0
  129. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/roadmap_archive.py +0 -0
  130. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/scaffold.py +0 -0
  131. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/triage.py +0 -0
  132. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/upgrade_merge_gate.py +0 -0
  133. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/loop/validate_event.py +0 -0
  134. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/__init__.py +0 -0
  135. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/adapters.py +0 -0
  136. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/artifact.py +0 -0
  137. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/autofix.py +0 -0
  138. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/autofix_invoke.py +0 -0
  139. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/autofix_run.py +0 -0
  140. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/autofix_state.py +0 -0
  141. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/diagnosis.py +0 -0
  142. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/durations.py +0 -0
  143. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/fingerprint.py +0 -0
  144. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/issues.py +0 -0
  145. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/providers/__init__.py +0 -0
  146. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/providers/_azure_auth.py +0 -0
  147. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  148. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/redaction.py +0 -0
  149. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse/monitor/schedule.py +0 -0
  150. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  151. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse_loop.egg-info/entry_points.txt +0 -0
  152. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse_loop.egg-info/requires.txt +0 -0
  153. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/specfuse_loop.egg-info/top_level.txt +0 -0
  154. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_abandon_feature_skill.py +0 -0
  155. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_abandoned_work_reported.py +0 -0
  156. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_abandoned_wu_strands_gate.py +0 -0
  157. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_adopt_feature.py +0 -0
  158. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_budget.py +0 -0
  159. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_bug_selection_dedup.py +0 -0
  160. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_default_features_root.py +0 -0
  161. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_driver_command.py +0 -0
  162. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_driver_invoke.py +0 -0
  163. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_driver_restart.py +0 -0
  164. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_escalation_on_issue.py +0 -0
  165. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_escalation_run_state.py +0 -0
  166. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_findings_seam.py +0 -0
  167. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_invoke_timeout.py +0 -0
  168. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_invoke_usage.py +0 -0
  169. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_item_worktree.py +0 -0
  170. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_lock.py +0 -0
  171. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_policy_key_ownership.py +0 -0
  172. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_policy_queue.py +0 -0
  173. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_policy_schema.py +0 -0
  174. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_policy_triage_dial.py +0 -0
  175. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_answers.py +0 -0
  176. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_bugs.py +0 -0
  177. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_feature.py +0 -0
  178. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_findings_autofix.py +0 -0
  179. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_findings_diagnose.py +0 -0
  180. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_isolation.py +0 -0
  181. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_provider_triage.py +0 -0
  182. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_queue_read.py +0 -0
  183. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_relays_driver_output.py +0 -0
  184. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_repo_detect.py +0 -0
  185. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_run.py +0 -0
  186. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_run_progress.py +0 -0
  187. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_seam.py +0 -0
  188. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_snapshot_refresh.py +0 -0
  189. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_agent_state.py +0 -0
  190. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_answer_escalation_skill.py +0 -0
  191. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_app_insights_adapters.py +0 -0
  192. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_app_insights_query_schema_check.py +0 -0
  193. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_app_insights_workspace_schema.py +0 -0
  194. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_archive_ref_reconciliation.py +0 -0
  195. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_eval.py +0 -0
  196. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_eval_lint_class.py +0 -0
  197. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_eval_wiring.py +0 -0
  198. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  199. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_sweep.py +0 -0
  200. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_sweep_gate.py +0 -0
  201. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_txn.py +0 -0
  202. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_arm_wiring.py +0 -0
  203. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_artifact_redaction.py +0 -0
  204. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attempt_note_untracked_files.py +0 -0
  205. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attempt_outcome_contract.py +0 -0
  206. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attempt_outcome_emission.py +0 -0
  207. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attention_nonwriting_guard.py +0 -0
  208. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attention_silence_section.py +0 -0
  209. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attention_skill_structure.py +0 -0
  210. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_attribution_halt_claims.py +0 -0
  211. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autoclose_deferral_visibility.py +0 -0
  212. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autoclose_gate_note.py +0 -0
  213. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  214. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autofix_invoke.py +0 -0
  215. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autofix_live.py +0 -0
  216. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autofix_predicate.py +0 -0
  217. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autofix_run.py +0 -0
  218. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autofix_state.py +0 -0
  219. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autosync.py +0 -0
  220. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autosync_consent.py +0 -0
  221. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autosync_firstrun.py +0 -0
  222. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autosync_no_cwd_leak.py +0 -0
  223. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_autosync_plugin.py +0 -0
  224. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_azure_credential_resolution.py +0 -0
  225. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_backend.py +0 -0
  226. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_baseline_escalation_message.py +0 -0
  227. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_baseline_persistence.py +0 -0
  228. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_baseline_probe.py +0 -0
  229. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_baseline_provenance.py +0 -0
  230. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_baseline_tree_hash_key.py +0 -0
  231. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bash_routing.py +0 -0
  232. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bats_suites_gated.py +0 -0
  233. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_binary_verdict.py +0 -0
  234. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_binding_block_advisory.py +0 -0
  235. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_binding_block_allocation.py +0 -0
  236. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_binding_block_budget.py +0 -0
  237. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_blind_run_is_not_drained.py +0 -0
  238. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  239. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  240. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bookkeeping_no_verify.py +0 -0
  241. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  242. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_ci_pending.py +0 -0
  243. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_guardrails.py +0 -0
  244. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_label_nonfatal.py +0 -0
  245. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_labels_registered.py +0 -0
  246. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_min_severity.py +0 -0
  247. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_no_checks_pending.py +0 -0
  248. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_policy_contract.py +0 -0
  249. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_pr_number_carried.py +0 -0
  250. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  251. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_run.py +0 -0
  252. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_spend.py +0 -0
  253. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_state.py +0 -0
  254. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  255. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_superseded.py +0 -0
  256. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_bug_lane_test_paths.py +0 -0
  257. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_build_provenance.py +0 -0
  258. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_caller_check_ratchet.py +0 -0
  259. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_carried_green_invalidated_by_diff.py +0 -0
  260. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_carry_accounting_close_and_judge.py +0 -0
  261. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_changed_file_test_selection.py +0 -0
  262. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_changelog_append_only.py +0 -0
  263. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_changelog_collection.py +0 -0
  264. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_changelog_release_wiring.py +0 -0
  265. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_changelog_schema.py +0 -0
  266. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_ci_conclusion_wait.py +0 -0
  267. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_ci_runs_every_gate.py +0 -0
  268. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_claude_resolution.py +0 -0
  269. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_close_b_pending_learnings.py +0 -0
  270. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_close_records_feature_oracle.py +0 -0
  271. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_close_wu_self_reference.py +0 -0
  272. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_closing_deliverable_guard.py +0 -0
  273. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_closing_guard_contracts.py +0 -0
  274. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_closing_guard_prediction.py +0 -0
  275. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_closing_learnings_destination.py +0 -0
  276. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_closing_requirements.py +0 -0
  277. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_closing_sessions_on_sonnet.py +0 -0
  278. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_coauthor_trailer_allowlist.py +0 -0
  279. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_conditional_reflection.py +0 -0
  280. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_console_encoding.py +0 -0
  281. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_convergent_iteration.py +0 -0
  282. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_convergent_wu_lint.py +0 -0
  283. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_correlation_id_override.py +0 -0
  284. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_correlation_id_two_surfaces.py +0 -0
  285. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_cost_lifetime.py +0 -0
  286. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_cost_tracking.py +0 -0
  287. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_criteria_per_gate_lint.py +0 -0
  288. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_criteria_state.py +0 -0
  289. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_criteria_worklist.py +0 -0
  290. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_decision_citation_lint.py +0 -0
  291. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_decision_followups_discharged.py +0 -0
  292. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_decision_override_lint.py +0 -0
  293. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_decisions_format.py +0 -0
  294. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_default_severity_aliases.py +0 -0
  295. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_degraded_oracle.py +0 -0
  296. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_deliverable_missing_note.py +0 -0
  297. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_deliverable_presence_gate.py +0 -0
  298. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_derive_agent_policy_review_mode.py +0 -0
  299. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_derive_agent_policy_skill.py +0 -0
  300. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_derive_monitoring_discovery.py +0 -0
  301. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_derive_monitoring_skill_registration.py +0 -0
  302. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_design_for_diagnosis_rule.py +0 -0
  303. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_deterministic_refusal_repeat.py +0 -0
  304. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_diagnose_headless.py +0 -0
  305. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_diagnose_issue_skill.py +0 -0
  306. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_diagnosis.py +0 -0
  307. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_diagnosis_roundtrip_live.py +0 -0
  308. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_dispatch_model_from_fix_scope.py +0 -0
  309. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
  310. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_dispatch_skeleton.py +0 -0
  311. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_dispatch_skills_index.py +0 -0
  312. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_distilled_accept_step.py +0 -0
  313. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_doctor.py +0 -0
  314. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_doctor_command_preflight.py +0 -0
  315. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_done_feature_gates.py +0 -0
  316. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_draft_feature_answers_mode.py +0 -0
  317. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_drafting_answer_gate.py +0 -0
  318. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_drafting_invoke.py +0 -0
  319. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_drafting_question_issue.py +0 -0
  320. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_drafting_questions.py +0 -0
  321. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_drafting_reply_shape.py +0 -0
  322. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_edit_detection.py +0 -0
  323. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  324. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_integration.py +0 -0
  325. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_lock.py +0 -0
  326. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_module_surface.py +0 -0
  327. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_restart_halt_surfaced.py +0 -0
  328. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_restart_halt_wiring.py +0 -0
  329. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_restart_hold.py +0 -0
  330. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_staleness_gate_summary.py +0 -0
  331. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_driver_staleness_warning.py +0 -0
  332. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_dry_run_no_branch_mutation.py +0 -0
  333. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_duration_tracking.py +0 -0
  334. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_empty_files_escalation.py +0 -0
  335. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_ensure_feature_branch.py +0 -0
  336. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_ensure_feature_branch_base.py +0 -0
  337. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_escalation_assignee_policy.py +0 -0
  338. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_escalation_contract.py +0 -0
  339. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_escalation_crash_containment.py +0 -0
  340. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_escalation_dedup_search.py +0 -0
  341. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_escalation_emit.py +0 -0
  342. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_event_gate_full_envelope.py +0 -0
  343. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_events_redaction.py +0 -0
  344. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_events_stats.py +0 -0
  345. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_extra_gates.py +0 -0
  346. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_failure_artifact_model.py +0 -0
  347. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_failure_signature_fail_prefix.py +0 -0
  348. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_failure_signature_names_the_test.py +0 -0
  349. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_failure_signature_skips_log_noise.py +0 -0
  350. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_branch_auto_sync.py +0 -0
  351. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_branch_squash_merge.py +0 -0
  352. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_branch_stale_base.py +0 -0
  353. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_id_resolution.py +0 -0
  354. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_oracle_e2e.py +0 -0
  355. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  356. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_feature_review_accumulation.py +0 -0
  357. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_filelock_portable.py +0 -0
  358. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_files_changed_auto_repair.py +0 -0
  359. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_find_pr_no_search_index.py +0 -0
  360. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fingerprint.py +0 -0
  361. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_bug_cwd_worktree.py +0 -0
  362. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_bug_diff_self_check.py +0 -0
  363. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_bug_headless.py +0 -0
  364. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_bug_reads_comments.py +0 -0
  365. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
  366. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_unit_insertion_e2e.py +0 -0
  367. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fix_unit_insertion_refused.py +0 -0
  368. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_fold_contract_documented.py +0 -0
  369. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_followups_artifact.py +0 -0
  370. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_force_full_close.py +0 -0
  371. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_frontmatter_scalar_quoting.py +0 -0
  372. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_broad_run.py +0 -0
  373. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_budget.py +0 -0
  374. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_budget_post_dispatch.py +0 -0
  375. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_commands_needs.py +0 -0
  376. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_eval.py +0 -0
  377. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_eval_calibration.py +0 -0
  378. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  379. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_eval_repo_flag.py +0 -0
  380. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_eval_terminal_wiring.py +0 -0
  381. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gate_report_verdict.py +0 -0
  382. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gh_backend.py +0 -0
  383. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gh_features.py +0 -0
  384. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_git_env_isolation.py +0 -0
  385. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_gitleaks_pinning.py +0 -0
  386. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_groom_backlog_skill.py +0 -0
  387. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_guard_refusal_failure_class.py +0 -0
  388. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  389. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_guard_repair_e2e.py +0 -0
  390. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_guard_repair_prompt.py +0 -0
  391. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_hashed_denylist.py +0 -0
  392. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_hashed_denylist_ci.py +0 -0
  393. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_headless_no_background_tasks.py +0 -0
  394. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_heartbeat.py +0 -0
  395. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_heartbeat_adapter.py +0 -0
  396. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_heartbeat_emission_contract.py +0 -0
  397. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_http_status_regression_adapter.py +0 -0
  398. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_http_status_regression_end_to_end.py +0 -0
  399. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_human_output_rule.py +0 -0
  400. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_human_work_unit.py +0 -0
  401. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_hygiene_boundary_guidance.py +0 -0
  402. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_hyphenated_gate_name.py +0 -0
  403. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_ignored_cleanup_before_probe.py +0 -0
  404. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_init_integration.py +0 -0
  405. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_installed_copy_driver_e2e.py +0 -0
  406. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_interpreter_normalization.py +0 -0
  407. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_close_path.py +0 -0
  408. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_cost.py +0 -0
  409. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_cost_fold_committed.py +0 -0
  410. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_module.py +0 -0
  411. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_path_registry.py +0 -0
  412. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_sees_dropped_produces.py +0 -0
  413. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judge_terminal_flip_state.py +0 -0
  414. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_judged_event_points_at_findings.py +0 -0
  415. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_label_description_length.py +0 -0
  416. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_label_provisioning_runner_contract.py +0 -0
  417. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_label_registry.py +0 -0
  418. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_label_registry_covers_consumers.py +0 -0
  419. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_language_aware_narrow_selection.py +0 -0
  420. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lazy_baseline_e2e.py +0 -0
  421. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_leak_findings_redaction.py +0 -0
  422. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_leak_scan.py +0 -0
  423. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_leak_scan_content.py +0 -0
  424. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_learnings_curate_vendored_promote.py +0 -0
  425. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_learnings_distil_cli.py +0 -0
  426. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_learnings_query.py +0 -0
  427. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_learnings_staging.py +0 -0
  428. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_learnings_suggest_signature_filter.py +0 -0
  429. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_learnings_weights.py +0 -0
  430. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  431. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lifecycle_integration.py +0 -0
  432. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lifetime_accounting.py +0 -0
  433. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_ac_observable.py +0 -0
  434. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_bare_produces_path.py +0 -0
  435. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_boundary_consistency.py +0 -0
  436. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_boundary_extraction.py +0 -0
  437. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_close_intermediate.py +0 -0
  438. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  439. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_close_wu.py +0 -0
  440. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_closing.py +0 -0
  441. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_closing_criteria.py +0 -0
  442. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_closing_criteria_pristine.py +0 -0
  443. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_correlation_id.py +0 -0
  444. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  445. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  446. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_feature_oracle_declared.py +0 -0
  447. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_gate_proportionality.py +0 -0
  448. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_monitoring.py +0 -0
  449. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_oracle_env.py +0 -0
  450. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_plan_contract_fields.py +0 -0
  451. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_plan_errors.py +0 -0
  452. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_plan_next_draft.py +0 -0
  453. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_plan_relative_import.py +0 -0
  454. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_plan_verdict_exempt.py +0 -0
  455. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_planning_sections.py +0 -0
  456. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_produces_driver_helper.py +0 -0
  457. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_produces_satisfiability.py +0 -0
  458. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_roadmap.py +0 -0
  459. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_sections.py +0 -0
  460. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  461. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_lint_verification_reference.py +0 -0
  462. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_live_tests_opt_in.py +0 -0
  463. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_load_bearing_close_lint.py +0 -0
  464. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_attempt_evidence.py +0 -0
  465. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_auto_archive.py +0 -0
  466. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_caveman_preamble.py +0 -0
  467. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_close_intermediate.py +0 -0
  468. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_criteria_skeleton.py +0 -0
  469. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_criteria_survival.py +0 -0
  470. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_defaults_by_type.py +0 -0
  471. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_effort.py +0 -0
  472. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_failure_note_cap.py +0 -0
  473. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_files_changed_guard.py +0 -0
  474. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_gate_budget.py +0 -0
  475. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_gate_needs.py +0 -0
  476. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_model_alias.py +0 -0
  477. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_orchestration.py +0 -0
  478. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_post_pass_invariant.py +0 -0
  479. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_reset_preserving_events.py +0 -0
  480. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_smoke_runner.py +0 -0
  481. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_unsandboxed.py +0 -0
  482. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_worklist_injection.py +0 -0
  483. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_loop_zero_token_guard.py +0 -0
  484. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  485. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_marker_dual_shape.py +0 -0
  486. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_maven_failure_excerpt.py +0 -0
  487. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_max_attempts_configurable.py +0 -0
  488. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_max_open_prs.py +0 -0
  489. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_migrate_legacy.py +0 -0
  490. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_miniyaml_equivalence.py +0 -0
  491. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_miniyaml_negative.py +0 -0
  492. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitor_cli.py +0 -0
  493. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitor_durations.py +0 -0
  494. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitor_issue_lifecycle.py +0 -0
  495. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitor_runner_surfaces.py +0 -0
  496. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  497. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitoring_cron_dialect.py +0 -0
  498. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitoring_example.py +0 -0
  499. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitoring_fenced_blocks.py +0 -0
  500. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_monitoring_seed.py +0 -0
  501. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_narrow_fallback_warning.py +0 -0
  502. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_needs_decision_outcome.py +0 -0
  503. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_no_pytest_subprocess.py +0 -0
  504. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_notify.py +0 -0
  505. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_notify_escalation.py +0 -0
  506. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_notify_sla.py +0 -0
  507. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_operator_escalation_rule.py +0 -0
  508. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_oracle_set_declared.py +0 -0
  509. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_oracle_unrunnable_attribution.py +0 -0
  510. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_per_run_item_cap_disclosure.py +0 -0
  511. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pick_feature_skill.py +0 -0
  512. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pin_honesty_and_integrity.py +0 -0
  513. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pin_marker_not_inherited.py +0 -0
  514. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pin_marker_spawn_sites.py +0 -0
  515. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_plan_baseline.py +0 -0
  516. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_planned_cost_lint.py +0 -0
  517. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_planning_cost_floor.py +0 -0
  518. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_plugin_prose_cross_refs.py +0 -0
  519. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_plugin_qualified_dispatch.py +0 -0
  520. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_policy_budgets_applied.py +0 -0
  521. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_policy_proposals.py +0 -0
  522. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_policy_review.py +0 -0
  523. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pr_checks_json_field.py +0 -0
  524. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pr_reference_in_title.py +0 -0
  525. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  526. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_prep_halt_operator_surface.py +0 -0
  527. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_prepare_feature.py +0 -0
  528. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_prepare_scaffold_sync.py +0 -0
  529. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_prerun_capture.py +0 -0
  530. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_prerun_oracles.py +0 -0
  531. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_prerun_wiring.py +0 -0
  532. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produced_document_verdict_guard.py +0 -0
  533. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_amendment_e2e.py +0 -0
  534. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_bracket_literal.py +0 -0
  535. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_bracket_wildcard.py +0 -0
  536. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_field.py +0 -0
  537. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_in_diff.py +0 -0
  538. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_justification.py +0 -0
  539. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_recursive_glob.py +0 -0
  540. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_repair_note.py +0 -0
  541. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_semantics_unified.py +0 -0
  542. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_produces_shape.py +0 -0
  543. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_progress_forward_note.py +0 -0
  544. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_progress_lines_end_to_end.py +0 -0
  545. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_promoted_issue_not_redispatched.py +0 -0
  546. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_promotion_ledger.py +0 -0
  547. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_provider_escalation_traces.py +0 -0
  548. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_provision_labels.py +0 -0
  549. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_queue_stalled_adapter.py +0 -0
  550. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  551. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_rearm_fold_marker.py +0 -0
  552. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_rearm_migration.py +0 -0
  553. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
  554. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_red_baseline_never_reused.py +0 -0
  555. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_red_on_base_fetch_and_reason.py +0 -0
  556. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_red_on_base_guardrail.py +0 -0
  557. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_red_on_base_overlay.py +0 -0
  558. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_red_on_base_probe.py +0 -0
  559. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_red_on_base_real_runner.py +0 -0
  560. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_redaction_windows_home.py +0 -0
  561. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_reescalation_posts_comment.py +0 -0
  562. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_reflection_required_signals.py +0 -0
  563. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replan_end_to_end.py +0 -0
  564. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replan_event_emission.py +0 -0
  565. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replan_note_collision.py +0 -0
  566. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replan_trigger.py +0 -0
  567. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replan_turn_contract.py +0 -0
  568. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replay_spin.py +0 -0
  569. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_replay_spin_failing_sets.py +0 -0
  570. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_required_checks.py +0 -0
  571. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_resolve_base.py +0 -0
  572. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_restart_cap_counts_progress.py +0 -0
  573. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_result_block.py +0 -0
  574. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_result_block_audience.py +0 -0
  575. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_result_block_near_miss.py +0 -0
  576. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_result_block_scalar.py +0 -0
  577. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_resume_command_build.py +0 -0
  578. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  579. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_add_skill.py +0 -0
  580. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_archive_cli.py +0 -0
  581. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_archive_shim.py +0 -0
  582. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_archive_skill.py +0 -0
  583. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  584. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_link_gate.py +0 -0
  585. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_roadmap_row_parser.py +0 -0
  586. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_ruff_verdict_lines.py +0 -0
  587. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_rule_import_convergence.py +0 -0
  588. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_run_level_failure_breaker.py +0 -0
  589. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_data_in_sync.py +0 -0
  590. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_doc_hygiene.py +0 -0
  591. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_docs.py +0 -0
  592. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_init.py +0 -0
  593. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_label_provisioning.py +0 -0
  594. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_manifest.py +0 -0
  595. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_resources.py +0 -0
  596. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_seed_sanity.py +0 -0
  597. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_skew_halts.py +0 -0
  598. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_upgrade.py +0 -0
  599. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_scaffold_wiring.py +0 -0
  600. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_schedule_dialect.py +0 -0
  601. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_seed_documents_prerun_keys.py +0 -0
  602. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_service_bus_dlq_adapter.py +0 -0
  603. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_session_env_preflight.py +0 -0
  604. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_aliases.py +0 -0
  605. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_backfill_apply.py +0 -0
  606. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_backfill_bugs_only.py +0 -0
  607. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_backfill_end_to_end.py +0 -0
  608. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_backfill_limit.py +0 -0
  609. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_backfill_marker.py +0 -0
  610. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_backfill_run.py +0 -0
  611. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_label_projection.py +0 -0
  612. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_reconciliation.py +0 -0
  613. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_severity_rubric.py +0 -0
  614. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_shipped_templates_parse.py +0 -0
  615. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_skill_discovery_links.py +0 -0
  616. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_skills_vendored_in_sync.py +0 -0
  617. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_spec_work_unit_rules.py +0 -0
  618. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_spinning_rearm_gate.py +0 -0
  619. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_spinning_repeat_same_failing_set.py +0 -0
  620. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_spinout_brief_end_to_end.py +0 -0
  621. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_spinout_brief_replan_option.py +0 -0
  622. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_squash_commit_hook_crash.py +0 -0
  623. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_stop_rationale_captured.py +0 -0
  624. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_stranded_halt_cause.py +0 -0
  625. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_surefire_run_level_signature.py +0 -0
  626. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_surefire_verdict_lines.py +0 -0
  627. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_template_closing_shapes.py +0 -0
  628. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_terminal_auto_close_changelog.py +0 -0
  629. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_terminal_flip_ownership.py +0 -0
  630. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_terminal_flips.py +0 -0
  631. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_terminal_gate_message.py +0 -0
  632. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_tiered_verification_e2e.py +0 -0
  633. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_timeout_kill_portable.py +0 -0
  634. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage.py +0 -0
  635. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_apply.py +0 -0
  636. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_projects_severity_label.py +0 -0
  637. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_severity_classify.py +0 -0
  638. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_severity_end_to_end.py +0 -0
  639. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_severity_write.py +0 -0
  640. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_skill_contract.py +0 -0
  641. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_triage_skips_agent_escalations.py +0 -0
  642. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_tsc_verdict_lines.py +0 -0
  643. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_umbrella_version_resolution.py +0 -0
  644. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
  645. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_unterminated_frontmatter.py +0 -0
  646. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_untracked_feature_folder.py +0 -0
  647. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_untracked_file_absorption.py +0 -0
  648. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_upgrade_integration.py +0 -0
  649. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_upgrade_merge_gate.py +0 -0
  650. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_validate_event.py +0 -0
  651. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_validate_event_driver_types.py +0 -0
  652. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_verdict_coupling.py +0 -0
  653. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_verify_empty_gate_set.py +0 -0
  654. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_version_consistency.py +0 -0
  655. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_version_skew.py +0 -0
  656. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_windows_gate_exec.py +0 -0
  657. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_worktree_restore.py +0 -0
  658. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/tests/test_wu_execution_metadata.py +0 -0
  659. {specfuse_loop-0.28.0 → specfuse_loop-0.28.2}/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.0
3
+ Version: 0.28.2
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.0"
7
+ version = "0.28.2"
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"
@@ -46,9 +46,6 @@ from specfuse.loop.labels import (
46
46
  from specfuse.loop import agent_policy, triage
47
47
  from specfuse.loop.build_provenance import warn_if_out_of_tree
48
48
 
49
- #: Stands in for T10's classification session (gate 3, walking skeleton).
50
- _STUB_SEVERITY = "medium"
51
-
52
49
 
53
50
  def _default_runner(argv: list, check: bool = False):
54
51
  return subprocess.run(argv, check=check, capture_output=True, text=True)
@@ -0,0 +1 @@
1
+ 0.28.2
@@ -127,7 +127,7 @@ SPECFUSE_DIR = Path(".specfuse")
127
127
  REPO_ROOT = SPECFUSE_DIR.parent
128
128
  FEATURES_DIR = SPECFUSE_DIR / "features"
129
129
  VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
130
- DRIVER_VERSION = "0.28.0"
130
+ DRIVER_VERSION = "0.28.2"
131
131
  # Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
132
132
  # version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
133
133
  # the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
@@ -241,7 +241,16 @@ def _default_transport_resolver(module, check_type: str, binding: Mapping[str, o
241
241
  )
242
242
 
243
243
 
244
- def _build_adapter(cls, *, component: str, environment: Mapping[str, object], check: Mapping[str, object], transport, now: datetime):
244
+ def _build_adapter(
245
+ cls,
246
+ *,
247
+ component: str,
248
+ environment: Mapping[str, object],
249
+ check: Mapping[str, object],
250
+ transport,
251
+ now: datetime,
252
+ since: Optional[datetime] = None,
253
+ ):
245
254
  kwargs = dict(
246
255
  component=component,
247
256
  environment=environment,
@@ -255,6 +264,10 @@ def _build_adapter(cls, *, component: str, environment: Mapping[str, object], ch
255
264
  deploy_signal=check.get("deploy_signal"),
256
265
  window_after_deploy=check.get("window_after_deploy"),
257
266
  codes=check.get("codes"),
267
+ # The harvest window (#3480): adapters that read event history
268
+ # accept it; those that need older history by design do not.
269
+ since=since,
270
+ until=now if since is not None else None,
258
271
  )
259
272
  return _call_filtered(cls, **kwargs)
260
273
 
@@ -266,6 +279,7 @@ def _process_check(
266
279
  environment: Mapping[str, object],
267
280
  transport_resolver: Callable,
268
281
  now: datetime,
282
+ since: Optional[datetime] = None,
269
283
  ):
270
284
  check_type = check.get("type")
271
285
  if check_type not in CHECK_TYPES:
@@ -290,7 +304,13 @@ def _process_check(
290
304
  adapter_cls = _find_adapter_class(module, check_type)
291
305
  transport = transport_resolver(module, check_type, binding)
292
306
  adapter = _build_adapter(
293
- adapter_cls, component=component_name, environment=environment, check=check, transport=transport, now=now
307
+ adapter_cls,
308
+ component=component_name,
309
+ environment=environment,
310
+ check=check,
311
+ transport=transport,
312
+ now=now,
313
+ since=since,
294
314
  )
295
315
  artifacts = list(adapter.fetch_failures())
296
316
  skipped = list(getattr(adapter, "skipped_targets", []))
@@ -437,6 +457,10 @@ def run_cycle(
437
457
  out = out if out is not None else sys.stdout
438
458
  now = now if now is not None else datetime.now(timezone.utc)
439
459
  gh_runner = gh_runner if gh_runner is not None else _default_runner
460
+ # Fingerprints already handed to `record_finding` this cycle (#3486): the
461
+ # `gh issue list` lookup can lag an issue created moments earlier, so a
462
+ # repeated fingerprint must not reach it twice.
463
+ filed_fingerprints: set = set()
440
464
 
441
465
  environments = config.get("environments") or {}
442
466
  components = config.get("components") or []
@@ -454,8 +478,10 @@ def run_cycle(
454
478
 
455
479
  summary: list = []
456
480
  total_findings = 0
481
+ any_errored = False
457
482
 
458
483
  for env_name in sorted(selected_envs):
484
+ env_errored = False
459
485
  environment = selected_envs[env_name]
460
486
  watermark_path = watermark_dir / f"{env_name}.json"
461
487
  since, fallback_reason = _read_watermark(watermark_path, lookback=lookback, now=now)
@@ -487,13 +513,27 @@ def run_cycle(
487
513
  findings: list = []
488
514
  skipped: list = []
489
515
  for check in component.get("checks") or []:
490
- artifacts, check_skipped = _process_check(
491
- check=check,
492
- component=component,
493
- environment=environment,
494
- transport_resolver=transport_resolver,
495
- now=now,
496
- )
516
+ # #3478: one check's failure — a provider SDK error, a missing
517
+ # credential, a permission the running identity lacks — must
518
+ # not blind every other check in the run. Report it, keep the
519
+ # environment's watermark where it was so the window is
520
+ # retried, and exit non-zero once the summary is printed.
521
+ try:
522
+ artifacts, check_skipped = _process_check(
523
+ check=check,
524
+ component=component,
525
+ environment=environment,
526
+ transport_resolver=transport_resolver,
527
+ now=now,
528
+ since=since,
529
+ )
530
+ except Exception as exc: # noqa: BLE001 — isolation is the point
531
+ env_errored = True
532
+ summary.append(
533
+ f"{env_name}/{component_name}: check {check.get('type')} errored — "
534
+ f"{type(exc).__name__}: {exc}"
535
+ )
536
+ continue
497
537
  findings.extend(artifacts)
498
538
  skipped.extend(check_skipped)
499
539
 
@@ -504,9 +544,13 @@ def run_cycle(
504
544
  for artifact in findings:
505
545
  summary.append(f" would file: {_describe(artifact)}")
506
546
  else:
507
- created = updated = throttled = 0
547
+ created = updated = throttled = repeats = 0
508
548
  for artifact in findings:
509
549
  fingerprint = fingerprint_artifact(artifact)
550
+ if fingerprint in filed_fingerprints:
551
+ repeats += 1
552
+ continue
553
+ filed_fingerprints.add(fingerprint)
510
554
  recorder = _CallRecorder(gh_runner)
511
555
  record_finding(fingerprint, artifact, repo, runner=recorder, now=now.timestamp())
512
556
  outcome = _classify(recorder)
@@ -516,10 +560,13 @@ def run_cycle(
516
560
  updated += 1
517
561
  else:
518
562
  throttled += 1
519
- summary.append(
563
+ line = (
520
564
  f"{env_name}/{component_name}: {len(findings)} finding(s) — "
521
565
  f"created {created}, updated {updated}, throttled {throttled}"
522
566
  )
567
+ if repeats:
568
+ line += f", {repeats} repeat(s) of an already-filed fingerprint"
569
+ summary.append(line)
523
570
 
524
571
  for skip in skipped:
525
572
  summary.append(
@@ -528,14 +575,20 @@ def run_cycle(
528
575
  f"{getattr(skip, 'reason', 'unknown reason')}"
529
576
  )
530
577
 
531
- if not dry_run:
578
+ if env_errored:
579
+ any_errored = True
580
+ summary.append(
581
+ f"environment {env_name}: watermark not advanced — a check errored; "
582
+ "its window is retried next cycle"
583
+ )
584
+ elif not dry_run:
532
585
  _write_watermark(watermark_path, now=now)
533
586
 
534
587
  summary.append(f"total: {total_findings} finding(s) across {len(selected_components)} component(s)")
535
588
  for line in summary:
536
589
  print(line, file=out)
537
590
 
538
- return 0
591
+ return 1 if any_errored else 0
539
592
 
540
593
 
541
594
  # ---------------------------------------------------------------------------
@@ -19,6 +19,7 @@ from __future__ import annotations
19
19
 
20
20
  import argparse
21
21
  import json
22
+ import re
22
23
  import sys
23
24
  from typing import Any
24
25
 
@@ -37,6 +38,19 @@ class AnalysisParseError(ValueError):
37
38
  """
38
39
 
39
40
 
41
+ #: One markdown code fence around the whole result, optionally tagged `json`.
42
+ #: Headless sessions asked for "exactly one JSON object" still fence it often
43
+ #: (#3489); anything outside the fence stays a parse error.
44
+ _FENCE_RE = re.compile(r"\A\s*```(?:json)?[ \t]*\n(?P<body>.*?)\n```\s*\Z", re.DOTALL)
45
+
46
+
47
+ def _strip_fence(raw: str) -> str:
48
+ match = _FENCE_RE.match(raw)
49
+ if match is None or "```" in match.group("body"):
50
+ return raw
51
+ return match.group("body")
52
+
53
+
40
54
  def parse_analysis(raw: str) -> Diagnosis:
41
55
  """Parse an analysis-step result (JSON text) into a `Diagnosis`.
42
56
 
@@ -45,7 +59,7 @@ def parse_analysis(raw: str) -> Diagnosis:
45
59
  required field, or a `fix_scope` outside `FIX_SCOPES`.
46
60
  """
47
61
  try:
48
- data: Any = json.loads(raw)
62
+ data: Any = json.loads(_strip_fence(raw))
49
63
  except json.JSONDecodeError as exc:
50
64
  raise AnalysisParseError(f"analysis result is not valid JSON: {exc}") from exc
51
65
 
@@ -69,7 +69,29 @@ class AppInsightsTransport(Protocol):
69
69
  ...
70
70
 
71
71
 
72
- def _error_logs_query(component: str) -> str:
72
+ def _kql_datetime(moment: datetime) -> str:
73
+ """A KQL ``datetime()`` literal at full precision. ``moment`` comes from
74
+ the harvester's own clock or watermark file, never from a returned row."""
75
+ text = moment.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
76
+ return f"datetime({text})"
77
+
78
+
79
+ def _harvest_window(since: Optional[datetime], until: Optional[datetime]) -> str:
80
+ """The harvest-window predicate (#3480), or nothing when no window was
81
+ given. Bounded on ``ingestion_time()`` rather than ``TimeGenerated`` so a
82
+ row stamped before the previous run but ingested after it is read by the
83
+ next run instead of falling between two windows."""
84
+ if since is None or until is None:
85
+ return ""
86
+ return (
87
+ f"| where ingestion_time() >= {_kql_datetime(since)} "
88
+ f"and ingestion_time() < {_kql_datetime(until)} "
89
+ )
90
+
91
+
92
+ def _error_logs_query(
93
+ component: str, since: Optional[datetime] = None, until: Optional[datetime] = None
94
+ ) -> str:
73
95
  """Built only from *component*, an operator-configured name — never from
74
96
  a telemetry query's returned rows.
75
97
 
@@ -81,16 +103,20 @@ def _error_logs_query(component: str) -> str:
81
103
  return (
82
104
  "AppExceptions "
83
105
  f"| where AppRoleName == '{component}' "
106
+ f"{_harvest_window(since, until)}"
84
107
  "| project timestamp = TimeGenerated, operation_Id = OperationId, "
85
108
  "operation_ParentId = ParentId, type = ExceptionType, "
86
109
  "outerMessage = OuterMessage"
87
110
  )
88
111
 
89
112
 
90
- def _http_5xx_query(component: str) -> str:
113
+ def _http_5xx_query(
114
+ component: str, since: Optional[datetime] = None, until: Optional[datetime] = None
115
+ ) -> str:
91
116
  return (
92
117
  "AppRequests "
93
118
  f"| where AppRoleName == '{component}' and ResultCode startswith '5' "
119
+ f"{_harvest_window(since, until)}"
94
120
  "| project timestamp = TimeGenerated, operation_Id = OperationId, "
95
121
  "name = Name, resultCode = ResultCode"
96
122
  )
@@ -105,13 +131,17 @@ class ErrorLogsAdapter:
105
131
  component: str,
106
132
  environment: Mapping[str, object],
107
133
  transport: AppInsightsTransport,
134
+ since: Optional[datetime] = None,
135
+ until: Optional[datetime] = None,
108
136
  ) -> None:
109
137
  self._component = component
110
138
  self._telemetry = resolve_telemetry(component, environment)
111
139
  self._transport = transport
140
+ self._since = since
141
+ self._until = until
112
142
 
113
143
  def fetch_failures(self) -> Iterator[FailureArtifact]:
114
- query = _error_logs_query(self._component)
144
+ query = _error_logs_query(self._component, self._since, self._until)
115
145
  rows = self._transport.run_query(query)
116
146
  for row in rows:
117
147
  yield self._build_artifact(row)
@@ -140,13 +170,17 @@ class Http5xxAdapter:
140
170
  component: str,
141
171
  environment: Mapping[str, object],
142
172
  transport: AppInsightsTransport,
173
+ since: Optional[datetime] = None,
174
+ until: Optional[datetime] = None,
143
175
  ) -> None:
144
176
  self._component = component
145
177
  self._telemetry = resolve_telemetry(component, environment)
146
178
  self._transport = transport
179
+ self._since = since
180
+ self._until = until
147
181
 
148
182
  def fetch_failures(self) -> Iterator[FailureArtifact]:
149
- query = _http_5xx_query(self._component)
183
+ query = _http_5xx_query(self._component, self._since, self._until)
150
184
  rows = self._transport.run_query(query)
151
185
  for row in rows:
152
186
  yield self._build_artifact(row)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.28.0
3
+ Version: 0.28.2
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
@@ -182,6 +182,7 @@ tests/test_agent_snapshot_refresh.py
182
182
  tests/test_agent_state.py
183
183
  tests/test_answer_escalation_skill.py
184
184
  tests/test_app_insights_adapters.py
185
+ tests/test_app_insights_harvest_window.py
185
186
  tests/test_app_insights_query_schema_check.py
186
187
  tests/test_app_insights_workspace_schema.py
187
188
  tests/test_archive_ref_reconciliation.py
@@ -294,6 +295,7 @@ tests/test_derive_monitoring_discovery.py
294
295
  tests/test_derive_monitoring_skill_registration.py
295
296
  tests/test_design_for_diagnosis_rule.py
296
297
  tests/test_deterministic_refusal_repeat.py
298
+ tests/test_diagnose_fenced_analysis.py
297
299
  tests/test_diagnose_headless.py
298
300
  tests/test_diagnose_issue_skill.py
299
301
  tests/test_diagnosis.py
@@ -482,9 +484,11 @@ tests/test_max_open_prs.py
482
484
  tests/test_migrate_legacy.py
483
485
  tests/test_miniyaml_equivalence.py
484
486
  tests/test_miniyaml_negative.py
487
+ tests/test_monitor_check_isolation.py
485
488
  tests/test_monitor_cli.py
486
489
  tests/test_monitor_durations.py
487
490
  tests/test_monitor_issue_lifecycle.py
491
+ tests/test_monitor_one_issue_per_fingerprint.py
488
492
  tests/test_monitor_runner_surfaces.py
489
493
  tests/test_monitoring_bootstrap_artifacts.py
490
494
  tests/test_monitoring_cron_dialect.py
@@ -0,0 +1,132 @@
1
+ # Copyright 2026 Specfuse Contributors
2
+ # Licensed under the Apache License, Version 2.0. See LICENSE.
3
+ """#3480: the App Insights `error-logs` and `http-5xx` checks must read only
4
+ the harvest window `run_cycle` computes — from the environment's watermark,
5
+ or `now - lookback` without one — not the workspace's whole retention.
6
+
7
+ The window is bounded on `ingestion_time()`, not `TimeGenerated`: a row
8
+ stamped just before the previous run but ingested after it is still picked
9
+ up by the next run instead of being skipped forever. `heartbeat` and
10
+ `http-status-regression` are deliberately not bounded by it: one needs the
11
+ last heartbeat however old, the other compares windows around a deployment.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import io
17
+ import tempfile
18
+ import unittest
19
+ from datetime import datetime, timedelta, timezone
20
+ from pathlib import Path
21
+
22
+ import specfuse.monitor.cli as cli
23
+
24
+ _NOW = datetime(2026, 10, 16, 10, 0, 0, 123456, tzinfo=timezone.utc)
25
+ _ENVIRONMENT = {
26
+ "telemetry": {"provider": "azure_app_insights", "credentials": {"api_key": "ACME_API_KEY"}},
27
+ }
28
+
29
+
30
+ def _config(checks):
31
+ return {
32
+ "environments": {"production": _ENVIRONMENT},
33
+ "components": [{
34
+ "name": "web-api",
35
+ "type": "api",
36
+ "runner": "local",
37
+ "diagnose": "manual",
38
+ "autofix": "off",
39
+ "checks": checks,
40
+ }],
41
+ }
42
+
43
+
44
+ class _Recorder:
45
+ def __init__(self):
46
+ self.queries: dict[str, str] = {}
47
+
48
+ def resolver(self, module, check_type, binding):
49
+ recorder = self
50
+
51
+ class _Transport:
52
+ def run_query(self, query):
53
+ recorder.queries[check_type] = query
54
+ return []
55
+
56
+ return _Transport()
57
+
58
+
59
+ _BOUNDED = [{"type": "error-logs"}, {"type": "http-5xx"}]
60
+
61
+
62
+ class TestTelemetryQueriesAreBoundedToTheHarvestWindow(unittest.TestCase):
63
+ def setUp(self):
64
+ self._tmpdir = tempfile.TemporaryDirectory()
65
+ self.addCleanup(self._tmpdir.cleanup)
66
+ self.watermark_dir = Path(self._tmpdir.name)
67
+
68
+ def _run(self, checks):
69
+ recorder = _Recorder()
70
+ cli.run_cycle(
71
+ _config(checks),
72
+ dry_run=True,
73
+ transport_resolver=recorder.resolver,
74
+ now=_NOW,
75
+ watermark_dir=self.watermark_dir,
76
+ out=io.StringIO(),
77
+ )
78
+ return recorder.queries
79
+
80
+ def _window(self, since):
81
+ return (
82
+ f"| where ingestion_time() >= datetime({since}) "
83
+ f"and ingestion_time() < datetime(2026-10-16T10:00:00.123456Z)"
84
+ )
85
+
86
+ def test_no_watermark_bounds_to_the_lookback(self):
87
+ queries = self._run(_BOUNDED)
88
+ since = "2026-10-15T10:00:00.123456Z" # _NOW - DEFAULT_LOOKBACK (24h)
89
+ for check_type in ("error-logs", "http-5xx"):
90
+ with self.subTest(check_type=check_type):
91
+ self.assertIn(self._window(since), queries[check_type])
92
+
93
+ def test_watermark_bounds_to_the_previous_run(self):
94
+ previous = _NOW - timedelta(minutes=15)
95
+ cli._write_watermark(self.watermark_dir / "production.json", now=previous)
96
+ queries = self._run(_BOUNDED)
97
+ for check_type in ("error-logs", "http-5xx"):
98
+ with self.subTest(check_type=check_type):
99
+ self.assertIn(self._window("2026-10-16T09:45:00.123456Z"), queries[check_type])
100
+
101
+ def test_window_filter_follows_the_table_scope(self):
102
+ # Bounded before projection, so the predicate reads the source table.
103
+ query = self._run([{"type": "error-logs"}])["error-logs"]
104
+ self.assertLess(query.index("ingestion_time()"), query.index("| project"))
105
+
106
+ def test_heartbeat_and_status_regression_are_not_window_bounded(self):
107
+ queries = self._run([
108
+ {"type": "heartbeat", "targets": [
109
+ {"name": "nightly", "cron": "0 2 * * *", "dialect": "standard-5", "timezone": "Etc/UTC"},
110
+ ]},
111
+ {"type": "http-status-regression", "deploy_signal": {"kind": "app-version"},
112
+ "window_after_deploy": "1h"},
113
+ ])
114
+ for check_type in ("heartbeat", "http-status-regression"):
115
+ with self.subTest(check_type=check_type):
116
+ self.assertNotIn("ingestion_time()", queries[check_type])
117
+
118
+
119
+ class TestBoundedQueriesMatchWorkspaceSchema(unittest.TestCase):
120
+ def test_bounded_queries_use_only_workspace_names(self):
121
+ from specfuse.monitor.providers import azure_app_insights as aai
122
+ from tests.test_app_insights_query_schema_check import schema_violations
123
+
124
+ since, until = _NOW - timedelta(hours=1), _NOW
125
+ for build in (aai._error_logs_query, aai._http_5xx_query):
126
+ with self.subTest(build=build.__name__):
127
+ query = build("backend-api", since, until)
128
+ self.assertEqual(schema_violations(query), [], query)
129
+
130
+
131
+ if __name__ == "__main__":
132
+ unittest.main()
@@ -0,0 +1,69 @@
1
+ # Copyright 2026 Specfuse Contributors
2
+ # Licensed under the Apache License, Version 2.0. See LICENSE.
3
+ """#3489: a headless analysis session often wraps its one JSON object in a
4
+ markdown code fence. `parse_analysis` must read a single surrounding fence as
5
+ the object it holds, and stay strict about everything else.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import unittest
12
+
13
+ from specfuse.monitor.diagnose_cli import AnalysisParseError, parse_analysis
14
+
15
+ _PAYLOAD = {
16
+ "root_cause": "The weekly job last ran before heartbeat emission shipped.",
17
+ "evidence": "AppRequests show successful runs; no ScheduleHeartbeat before 2026-10-14.",
18
+ "candidate_fix": "None in the component; wait for the next firing.",
19
+ "confidence": 0.7,
20
+ "fix_scope": "external",
21
+ }
22
+ _JSON = json.dumps(_PAYLOAD, indent=2)
23
+
24
+
25
+ class TestFencedAnalysisParses(unittest.TestCase):
26
+ def _assert_parses(self, raw):
27
+ diagnosis = parse_analysis(raw)
28
+ self.assertEqual(diagnosis.fix_scope, "external")
29
+ self.assertEqual(diagnosis.confidence, 0.7)
30
+ self.assertEqual(diagnosis.root_cause, _PAYLOAD["root_cause"])
31
+
32
+ def test_json_fence_parses(self):
33
+ self._assert_parses(f"```json\n{_JSON}\n```")
34
+
35
+ def test_bare_fence_parses(self):
36
+ self._assert_parses(f"```\n{_JSON}\n```")
37
+
38
+ def test_fence_with_surrounding_whitespace_parses(self):
39
+ self._assert_parses(f"\n\n```json\n{_JSON}\n```\n")
40
+
41
+ def test_unfenced_still_parses(self):
42
+ self._assert_parses(_JSON)
43
+
44
+
45
+ class TestStillStrict(unittest.TestCase):
46
+ def test_prose_before_fence_is_rejected(self):
47
+ with self.assertRaises(AnalysisParseError):
48
+ parse_analysis(f"Here is the diagnosis:\n```json\n{_JSON}\n```")
49
+
50
+ def test_prose_after_fence_is_rejected(self):
51
+ with self.assertRaises(AnalysisParseError):
52
+ parse_analysis(f"```json\n{_JSON}\n```\nLet me know if you need more.")
53
+
54
+ def test_two_fenced_objects_are_rejected(self):
55
+ with self.assertRaises(AnalysisParseError):
56
+ parse_analysis(f"```json\n{_JSON}\n```\n```json\n{_JSON}\n```")
57
+
58
+ def test_unterminated_fence_is_rejected(self):
59
+ with self.assertRaises(AnalysisParseError):
60
+ parse_analysis(f"```json\n{_JSON}\n")
61
+
62
+ def test_fenced_payload_missing_field_is_rejected(self):
63
+ partial = {k: v for k, v in _PAYLOAD.items() if k != "fix_scope"}
64
+ with self.assertRaises(AnalysisParseError):
65
+ parse_analysis(f"```json\n{json.dumps(partial)}\n```")
66
+
67
+
68
+ if __name__ == "__main__":
69
+ unittest.main()