specfuse-loop 0.22.1__tar.gz → 0.22.3__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 (609) hide show
  1. {specfuse_loop-0.22.1/specfuse_loop.egg-info → specfuse_loop-0.22.3}/PKG-INFO +1 -1
  2. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/pyproject.toml +1 -1
  3. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/severity_backfill.py +96 -10
  4. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/agent_policy.py +12 -1
  5. specfuse_loop-0.22.3/specfuse/loop/data/VERSION +1 -0
  6. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/labels.py +69 -1
  7. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/loop.py +189 -10
  8. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/triage.py +87 -1
  9. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3/specfuse_loop.egg-info}/PKG-INFO +1 -1
  10. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/SOURCES.txt +7 -0
  11. specfuse_loop-0.22.3/tests/test_attribution_halt_claims.py +139 -0
  12. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_escalation_message.py +18 -5
  13. specfuse_loop-0.22.3/tests/test_hygiene_boundary_guidance.py +88 -0
  14. specfuse_loop-0.22.3/tests/test_ignored_cleanup_before_probe.py +292 -0
  15. specfuse_loop-0.22.3/tests/test_per_run_item_cap_disclosure.py +80 -0
  16. specfuse_loop-0.22.3/tests/test_plugin_prose_cross_refs.py +80 -0
  17. specfuse_loop-0.22.3/tests/test_severity_backfill_end_to_end.py +215 -0
  18. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_run.py +158 -0
  19. specfuse_loop-0.22.3/tests/test_severity_label_projection.py +138 -0
  20. specfuse_loop-0.22.3/tests/test_severity_reconciliation.py +177 -0
  21. specfuse_loop-0.22.1/specfuse/loop/data/VERSION +0 -1
  22. specfuse_loop-0.22.1/tests/test_severity_backfill_end_to_end.py +0 -86
  23. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/LICENSE +0 -0
  24. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/NOTICE +0 -0
  25. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/README.md +0 -0
  26. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/setup.cfg +0 -0
  27. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/__init__.py +0 -0
  28. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/budget.py +0 -0
  29. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/diagnose_invoke.py +0 -0
  30. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/drafting_answers.py +0 -0
  31. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/drafting_invoke.py +0 -0
  32. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/drafting_questions.py +0 -0
  33. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/driver_command.py +0 -0
  34. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/driver_invoke.py +0 -0
  35. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/invoke.py +0 -0
  36. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/monitoring_read.py +0 -0
  37. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/__init__.py +0 -0
  38. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/answers.py +0 -0
  39. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/bugs.py +0 -0
  40. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/feature.py +0 -0
  41. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/findings_autofix.py +0 -0
  42. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/findings_diagnose.py +0 -0
  43. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/providers/triage.py +0 -0
  44. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/queue_read.py +0 -0
  45. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/repo_detect.py +0 -0
  46. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/run.py +0 -0
  47. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/state.py +0 -0
  48. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/triage_invoke.py +0 -0
  49. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/agent/worktree.py +0 -0
  50. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/__init__.py +0 -0
  51. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/_filelock.py +0 -0
  52. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/_miniyaml.py +0 -0
  53. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/_wu_sections.py +0 -0
  54. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/adopt_feature.py +0 -0
  55. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/arm_eval.py +0 -0
  56. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/arm_sweep.py +0 -0
  57. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/arm_txn.py +0 -0
  58. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/bug_lane.py +0 -0
  59. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/bug_lane_run.py +0 -0
  60. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/bug_lane_state.py +0 -0
  61. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/build_provenance.py +0 -0
  62. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/caller_check.py +0 -0
  63. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/changelog.py +0 -0
  64. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/closing_requirements.py +0 -0
  65. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/cost.py +0 -0
  66. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/criteria_state.py +0 -0
  67. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  68. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  69. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  70. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  71. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  72. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  73. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/getting-started.md +0 -0
  74. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/glossary.md +0 -0
  75. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/methodology.md +0 -0
  76. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/docs/skills.md +0 -0
  77. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/gitignore.snippet +0 -0
  78. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  79. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  80. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/monitoring.yml.example +0 -0
  81. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/roadmap.template.md +0 -0
  82. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/close-discipline.md +0 -0
  83. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  84. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  85. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/human-output.md +0 -0
  86. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/never-touch.md +0 -0
  87. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  88. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  89. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/result-contract.md +0 -0
  90. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  91. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  92. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/rules-local/README.md +0 -0
  93. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  94. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/event.schema.json +0 -0
  95. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  96. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  97. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  98. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  99. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  100. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/GATE.template.md +0 -0
  101. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  102. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  103. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/templates/WU.template.md +0 -0
  104. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/verification.yml.example +0 -0
  105. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  106. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/decisions_format.py +0 -0
  107. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/driver_edit.py +0 -0
  108. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/escalation.py +0 -0
  109. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/events_stats.py +0 -0
  110. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gate_commands.py +0 -0
  111. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gate_eval.py +0 -0
  112. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gh_backend.py +0 -0
  113. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/gh_features.py +0 -0
  114. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/heartbeat.py +0 -0
  115. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/judge.py +0 -0
  116. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/learnings_distil.py +0 -0
  117. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/learnings_query.py +0 -0
  118. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_closing.py +0 -0
  119. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_monitoring.py +0 -0
  120. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_plan.py +0 -0
  121. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/lint_roadmap.py +0 -0
  122. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/notify.py +0 -0
  123. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/notify_escalation.py +0 -0
  124. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/notify_sla.py +0 -0
  125. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/plan_baseline.py +0 -0
  126. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/policy_proposals.py +0 -0
  127. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/policy_review.py +0 -0
  128. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/prerun.py +0 -0
  129. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/prerun_capture.py +0 -0
  130. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/promotion.py +0 -0
  131. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/rearm_migration.py +0 -0
  132. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/replay_spin.py +0 -0
  133. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/roadmap_archive.py +0 -0
  134. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/scaffold.py +0 -0
  135. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/upgrade_merge_gate.py +0 -0
  136. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/loop/validate_event.py +0 -0
  137. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/__init__.py +0 -0
  138. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/adapters.py +0 -0
  139. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/artifact.py +0 -0
  140. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix.py +0 -0
  141. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix_invoke.py +0 -0
  142. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix_run.py +0 -0
  143. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/autofix_state.py +0 -0
  144. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/cli.py +0 -0
  145. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/diagnose_cli.py +0 -0
  146. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/diagnosis.py +0 -0
  147. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/fingerprint.py +0 -0
  148. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/issues.py +0 -0
  149. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/__init__.py +0 -0
  150. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/_azure_auth.py +0 -0
  151. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  152. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  153. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/redaction.py +0 -0
  154. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse/monitor/schedule.py +0 -0
  155. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  156. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/entry_points.txt +0 -0
  157. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/requires.txt +0 -0
  158. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/specfuse_loop.egg-info/top_level.txt +0 -0
  159. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_abandon_feature_skill.py +0 -0
  160. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_abandoned_work_reported.py +0 -0
  161. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_abandoned_wu_strands_gate.py +0 -0
  162. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_adopt_feature.py +0 -0
  163. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_budget.py +0 -0
  164. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_bug_selection_dedup.py +0 -0
  165. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_default_features_root.py +0 -0
  166. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_driver_command.py +0 -0
  167. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_driver_invoke.py +0 -0
  168. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_driver_restart.py +0 -0
  169. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_escalation_on_issue.py +0 -0
  170. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_escalation_run_state.py +0 -0
  171. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_findings_seam.py +0 -0
  172. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_invoke_timeout.py +0 -0
  173. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_invoke_usage.py +0 -0
  174. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_item_worktree.py +0 -0
  175. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_lock.py +0 -0
  176. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_key_ownership.py +0 -0
  177. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_queue.py +0 -0
  178. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_schema.py +0 -0
  179. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_policy_triage_dial.py +0 -0
  180. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_answers.py +0 -0
  181. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_bugs.py +0 -0
  182. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_feature.py +0 -0
  183. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_findings_autofix.py +0 -0
  184. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_findings_diagnose.py +0 -0
  185. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_isolation.py +0 -0
  186. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_provider_triage.py +0 -0
  187. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_queue_read.py +0 -0
  188. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_relays_driver_output.py +0 -0
  189. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_repo_detect.py +0 -0
  190. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_run.py +0 -0
  191. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_run_progress.py +0 -0
  192. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_seam.py +0 -0
  193. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_snapshot_refresh.py +0 -0
  194. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_agent_state.py +0 -0
  195. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_answer_escalation_skill.py +0 -0
  196. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_app_insights_adapters.py +0 -0
  197. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_archive_ref_reconciliation.py +0 -0
  198. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_eval.py +0 -0
  199. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_eval_lint_class.py +0 -0
  200. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_eval_wiring.py +0 -0
  201. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  202. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_sweep.py +0 -0
  203. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_sweep_gate.py +0 -0
  204. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_txn.py +0 -0
  205. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_arm_wiring.py +0 -0
  206. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_artifact_redaction.py +0 -0
  207. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attempt_note_untracked_files.py +0 -0
  208. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attempt_outcome_contract.py +0 -0
  209. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attempt_outcome_emission.py +0 -0
  210. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attention_nonwriting_guard.py +0 -0
  211. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attention_silence_section.py +0 -0
  212. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_attention_skill_structure.py +0 -0
  213. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autoclose_deferral_visibility.py +0 -0
  214. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autoclose_gate_note.py +0 -0
  215. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  216. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_invoke.py +0 -0
  217. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_live.py +0 -0
  218. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_predicate.py +0 -0
  219. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_run.py +0 -0
  220. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autofix_state.py +0 -0
  221. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync.py +0 -0
  222. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_consent.py +0 -0
  223. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_firstrun.py +0 -0
  224. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_no_cwd_leak.py +0 -0
  225. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_autosync_plugin.py +0 -0
  226. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_azure_credential_resolution.py +0 -0
  227. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_backend.py +0 -0
  228. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_persistence.py +0 -0
  229. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_probe.py +0 -0
  230. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_provenance.py +0 -0
  231. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_baseline_tree_hash_key.py +0 -0
  232. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bash_routing.py +0 -0
  233. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bats_suites_gated.py +0 -0
  234. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binary_verdict.py +0 -0
  235. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binding_block_advisory.py +0 -0
  236. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binding_block_allocation.py +0 -0
  237. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_binding_block_budget.py +0 -0
  238. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  239. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  240. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bookkeeping_no_verify.py +0 -0
  241. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  242. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_ci_pending.py +0 -0
  243. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_guardrails.py +0 -0
  244. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_label_nonfatal.py +0 -0
  245. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_labels_registered.py +0 -0
  246. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_min_severity.py +0 -0
  247. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_policy_contract.py +0 -0
  248. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_pr_number_carried.py +0 -0
  249. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  250. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_run.py +0 -0
  251. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_state.py +0 -0
  252. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  253. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_bug_lane_test_paths.py +0 -0
  254. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_build_provenance.py +0 -0
  255. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_caller_check_ratchet.py +0 -0
  256. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changed_file_test_selection.py +0 -0
  257. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_append_only.py +0 -0
  258. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_collection.py +0 -0
  259. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_release_wiring.py +0 -0
  260. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_changelog_schema.py +0 -0
  261. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ci_conclusion_wait.py +0 -0
  262. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ci_runs_every_gate.py +0 -0
  263. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_claude_resolution.py +0 -0
  264. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_close_b_pending_learnings.py +0 -0
  265. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_close_records_feature_oracle.py +0 -0
  266. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_close_wu_self_reference.py +0 -0
  267. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_deliverable_guard.py +0 -0
  268. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_guard_contracts.py +0 -0
  269. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_guard_prediction.py +0 -0
  270. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_learnings_destination.py +0 -0
  271. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_closing_requirements.py +0 -0
  272. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_coauthor_trailer_allowlist.py +0 -0
  273. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_conditional_reflection.py +0 -0
  274. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_console_encoding.py +0 -0
  275. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_convergent_iteration.py +0 -0
  276. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_convergent_wu_lint.py +0 -0
  277. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_correlation_id_override.py +0 -0
  278. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_correlation_id_two_surfaces.py +0 -0
  279. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_cost_lifetime.py +0 -0
  280. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_cost_tracking.py +0 -0
  281. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_criteria_per_gate_lint.py +0 -0
  282. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_criteria_state.py +0 -0
  283. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_criteria_worklist.py +0 -0
  284. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decision_citation_lint.py +0 -0
  285. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decision_followups_discharged.py +0 -0
  286. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decision_override_lint.py +0 -0
  287. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_decisions_format.py +0 -0
  288. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_default_severity_aliases.py +0 -0
  289. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_degraded_oracle.py +0 -0
  290. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_deliverable_missing_note.py +0 -0
  291. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_deliverable_presence_gate.py +0 -0
  292. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_agent_policy_review_mode.py +0 -0
  293. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_agent_policy_skill.py +0 -0
  294. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_monitoring_discovery.py +0 -0
  295. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_derive_monitoring_skill_registration.py +0 -0
  296. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_design_for_diagnosis_rule.py +0 -0
  297. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_deterministic_refusal_repeat.py +0 -0
  298. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnose_headless.py +0 -0
  299. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnose_issue_skill.py +0 -0
  300. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnosis.py +0 -0
  301. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_diagnosis_roundtrip_live.py +0 -0
  302. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_dispatch_skeleton.py +0 -0
  303. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_distilled_accept_step.py +0 -0
  304. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_doctor.py +0 -0
  305. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_doctor_command_preflight.py +0 -0
  306. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_done_feature_gates.py +0 -0
  307. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_draft_feature_answers_mode.py +0 -0
  308. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_answer_gate.py +0 -0
  309. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_invoke.py +0 -0
  310. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_question_issue.py +0 -0
  311. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_questions.py +0 -0
  312. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_drafting_reply_shape.py +0 -0
  313. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_edit_detection.py +0 -0
  314. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  315. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_integration.py +0 -0
  316. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_lock.py +0 -0
  317. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_module_surface.py +0 -0
  318. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_restart_halt_surfaced.py +0 -0
  319. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_restart_halt_wiring.py +0 -0
  320. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_restart_hold.py +0 -0
  321. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_staleness_gate_summary.py +0 -0
  322. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_driver_staleness_warning.py +0 -0
  323. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_dry_run_no_branch_mutation.py +0 -0
  324. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_duration_tracking.py +0 -0
  325. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_empty_files_escalation.py +0 -0
  326. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ensure_feature_branch.py +0 -0
  327. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ensure_feature_branch_base.py +0 -0
  328. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_assignee_policy.py +0 -0
  329. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_contract.py +0 -0
  330. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_crash_containment.py +0 -0
  331. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_dedup_search.py +0 -0
  332. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_escalation_emit.py +0 -0
  333. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_event_gate_full_envelope.py +0 -0
  334. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_events_redaction.py +0 -0
  335. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_events_stats.py +0 -0
  336. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_extra_gates.py +0 -0
  337. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_failure_artifact_model.py +0 -0
  338. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_failure_signature_fail_prefix.py +0 -0
  339. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_failure_signature_skips_log_noise.py +0 -0
  340. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_branch_auto_sync.py +0 -0
  341. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_branch_squash_merge.py +0 -0
  342. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_branch_stale_base.py +0 -0
  343. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_id_resolution.py +0 -0
  344. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_oracle_e2e.py +0 -0
  345. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  346. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_feature_review_accumulation.py +0 -0
  347. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_filelock_portable.py +0 -0
  348. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_files_changed_auto_repair.py +0 -0
  349. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_find_pr_no_search_index.py +0 -0
  350. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fingerprint.py +0 -0
  351. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fix_bug_diff_self_check.py +0 -0
  352. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fix_bug_headless.py +0 -0
  353. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fix_bug_reads_comments.py +0 -0
  354. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_fold_contract_documented.py +0 -0
  355. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_followups_artifact.py +0 -0
  356. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_force_full_close.py +0 -0
  357. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_frontmatter_scalar_quoting.py +0 -0
  358. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_broad_run.py +0 -0
  359. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_budget.py +0 -0
  360. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_budget_post_dispatch.py +0 -0
  361. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_commands_needs.py +0 -0
  362. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval.py +0 -0
  363. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_calibration.py +0 -0
  364. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  365. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_repo_flag.py +0 -0
  366. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_eval_terminal_wiring.py +0 -0
  367. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gate_report_verdict.py +0 -0
  368. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gh_backend.py +0 -0
  369. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gh_features.py +0 -0
  370. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_git_env_isolation.py +0 -0
  371. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_gitleaks_pinning.py +0 -0
  372. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_groom_backlog_skill.py +0 -0
  373. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_refusal_failure_class.py +0 -0
  374. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  375. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_repair_e2e.py +0 -0
  376. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_guard_repair_prompt.py +0 -0
  377. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_hashed_denylist.py +0 -0
  378. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_hashed_denylist_ci.py +0 -0
  379. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_heartbeat.py +0 -0
  380. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_heartbeat_adapter.py +0 -0
  381. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_human_output_rule.py +0 -0
  382. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_human_work_unit.py +0 -0
  383. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_hyphenated_gate_name.py +0 -0
  384. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_init_integration.py +0 -0
  385. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_installed_copy_driver_e2e.py +0 -0
  386. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_interpreter_normalization.py +0 -0
  387. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_close_path.py +0 -0
  388. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_cost.py +0 -0
  389. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_cost_fold_committed.py +0 -0
  390. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_module.py +0 -0
  391. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_path_registry.py +0 -0
  392. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_judge_terminal_flip_state.py +0 -0
  393. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_label_provisioning_runner_contract.py +0 -0
  394. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_label_registry.py +0 -0
  395. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_label_registry_covers_consumers.py +0 -0
  396. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_language_aware_narrow_selection.py +0 -0
  397. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lazy_baseline_e2e.py +0 -0
  398. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_leak_findings_redaction.py +0 -0
  399. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_leak_scan.py +0 -0
  400. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_leak_scan_content.py +0 -0
  401. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_curate_vendored_promote.py +0 -0
  402. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_distil_cli.py +0 -0
  403. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_query.py +0 -0
  404. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_staging.py +0 -0
  405. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_suggest_signature_filter.py +0 -0
  406. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_learnings_weights.py +0 -0
  407. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  408. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lifecycle_integration.py +0 -0
  409. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lifetime_accounting.py +0 -0
  410. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_ac_observable.py +0 -0
  411. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_bare_produces_path.py +0 -0
  412. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_boundary_consistency.py +0 -0
  413. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_boundary_extraction.py +0 -0
  414. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_close_intermediate.py +0 -0
  415. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  416. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_close_wu.py +0 -0
  417. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_closing.py +0 -0
  418. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_closing_criteria.py +0 -0
  419. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_closing_criteria_pristine.py +0 -0
  420. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_correlation_id.py +0 -0
  421. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  422. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  423. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_feature_oracle_declared.py +0 -0
  424. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_gate_proportionality.py +0 -0
  425. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_monitoring.py +0 -0
  426. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_oracle_env.py +0 -0
  427. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_contract_fields.py +0 -0
  428. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_errors.py +0 -0
  429. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_next_draft.py +0 -0
  430. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_relative_import.py +0 -0
  431. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_plan_verdict_exempt.py +0 -0
  432. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_planning_sections.py +0 -0
  433. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_produces_driver_helper.py +0 -0
  434. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_produces_satisfiability.py +0 -0
  435. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_roadmap.py +0 -0
  436. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_sections.py +0 -0
  437. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  438. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_lint_verification_reference.py +0 -0
  439. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_live_tests_opt_in.py +0 -0
  440. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_load_bearing_close_lint.py +0 -0
  441. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_attempt_evidence.py +0 -0
  442. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_auto_archive.py +0 -0
  443. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_caveman_preamble.py +0 -0
  444. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_close_intermediate.py +0 -0
  445. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_criteria_skeleton.py +0 -0
  446. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_criteria_survival.py +0 -0
  447. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_defaults_by_type.py +0 -0
  448. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_effort.py +0 -0
  449. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_failure_note_cap.py +0 -0
  450. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_files_changed_guard.py +0 -0
  451. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_gate_budget.py +0 -0
  452. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_gate_needs.py +0 -0
  453. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_model_alias.py +0 -0
  454. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_orchestration.py +0 -0
  455. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_post_pass_invariant.py +0 -0
  456. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_reset_preserving_events.py +0 -0
  457. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_smoke_runner.py +0 -0
  458. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_unsandboxed.py +0 -0
  459. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_worklist_injection.py +0 -0
  460. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_loop_zero_token_guard.py +0 -0
  461. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  462. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_marker_dual_shape.py +0 -0
  463. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_maven_failure_excerpt.py +0 -0
  464. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_max_attempts_configurable.py +0 -0
  465. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_max_open_prs.py +0 -0
  466. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_migrate_legacy.py +0 -0
  467. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_miniyaml_equivalence.py +0 -0
  468. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_miniyaml_negative.py +0 -0
  469. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitor_cli.py +0 -0
  470. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitor_issue_lifecycle.py +0 -0
  471. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitor_runner_surfaces.py +0 -0
  472. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  473. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_cron_dialect.py +0 -0
  474. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_example.py +0 -0
  475. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_fenced_blocks.py +0 -0
  476. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_monitoring_seed.py +0 -0
  477. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_narrow_fallback_warning.py +0 -0
  478. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_no_pytest_subprocess.py +0 -0
  479. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_notify.py +0 -0
  480. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_notify_escalation.py +0 -0
  481. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_notify_sla.py +0 -0
  482. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_operator_escalation_rule.py +0 -0
  483. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_oracle_set_declared.py +0 -0
  484. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_oracle_unrunnable_attribution.py +0 -0
  485. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pick_feature_skill.py +0 -0
  486. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pin_honesty_and_integrity.py +0 -0
  487. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pin_marker_not_inherited.py +0 -0
  488. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pin_marker_spawn_sites.py +0 -0
  489. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_plan_baseline.py +0 -0
  490. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_planned_cost_lint.py +0 -0
  491. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_planning_cost_floor.py +0 -0
  492. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_plugin_qualified_dispatch.py +0 -0
  493. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_policy_budgets_applied.py +0 -0
  494. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_policy_proposals.py +0 -0
  495. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_policy_review.py +0 -0
  496. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pr_checks_json_field.py +0 -0
  497. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pr_reference_in_title.py +0 -0
  498. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  499. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prep_halt_operator_surface.py +0 -0
  500. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prepare_feature.py +0 -0
  501. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prepare_scaffold_sync.py +0 -0
  502. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prerun_capture.py +0 -0
  503. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prerun_oracles.py +0 -0
  504. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_prerun_wiring.py +0 -0
  505. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produced_document_verdict_guard.py +0 -0
  506. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_bracket_literal.py +0 -0
  507. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_bracket_wildcard.py +0 -0
  508. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_field.py +0 -0
  509. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_in_diff.py +0 -0
  510. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_justification.py +0 -0
  511. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_recursive_glob.py +0 -0
  512. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_semantics_unified.py +0 -0
  513. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_produces_shape.py +0 -0
  514. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_progress_forward_note.py +0 -0
  515. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_progress_lines_end_to_end.py +0 -0
  516. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_promotion_ledger.py +0 -0
  517. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_provider_escalation_traces.py +0 -0
  518. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_provision_labels.py +0 -0
  519. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_queue_stalled_adapter.py +0 -0
  520. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  521. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rearm_fold_marker.py +0 -0
  522. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rearm_migration.py +0 -0
  523. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_red_baseline_never_reused.py +0 -0
  524. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_redaction_windows_home.py +0 -0
  525. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_reflection_required_signals.py +0 -0
  526. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_end_to_end.py +0 -0
  527. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_event_emission.py +0 -0
  528. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_note_collision.py +0 -0
  529. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_trigger.py +0 -0
  530. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replan_turn_contract.py +0 -0
  531. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_replay_spin.py +0 -0
  532. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_required_checks.py +0 -0
  533. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_resolve_base.py +0 -0
  534. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_restart_cap_counts_progress.py +0 -0
  535. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_result_block.py +0 -0
  536. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_result_block_audience.py +0 -0
  537. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_resume_command_build.py +0 -0
  538. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  539. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_add_skill.py +0 -0
  540. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_cli.py +0 -0
  541. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_shim.py +0 -0
  542. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_skill.py +0 -0
  543. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  544. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_link_gate.py +0 -0
  545. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_roadmap_row_parser.py +0 -0
  546. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_ruff_verdict_lines.py +0 -0
  547. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_rule_import_convergence.py +0 -0
  548. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_run_level_failure_breaker.py +0 -0
  549. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_data_in_sync.py +0 -0
  550. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_doc_hygiene.py +0 -0
  551. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_docs.py +0 -0
  552. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_init.py +0 -0
  553. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_label_provisioning.py +0 -0
  554. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_manifest.py +0 -0
  555. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_resources.py +0 -0
  556. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_seed_sanity.py +0 -0
  557. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_skew_halts.py +0 -0
  558. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_upgrade.py +0 -0
  559. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_scaffold_wiring.py +0 -0
  560. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_schedule_dialect.py +0 -0
  561. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_seed_documents_prerun_keys.py +0 -0
  562. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_service_bus_dlq_adapter.py +0 -0
  563. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_session_env_preflight.py +0 -0
  564. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_aliases.py +0 -0
  565. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_apply.py +0 -0
  566. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_limit.py +0 -0
  567. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_backfill_marker.py +0 -0
  568. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_severity_rubric.py +0 -0
  569. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_shipped_templates_parse.py +0 -0
  570. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_skill_discovery_links.py +0 -0
  571. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_skills_vendored_in_sync.py +0 -0
  572. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_spinning_rearm_gate.py +0 -0
  573. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_spinout_brief_end_to_end.py +0 -0
  574. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_spinout_brief_replan_option.py +0 -0
  575. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_squash_commit_hook_crash.py +0 -0
  576. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_stop_rationale_captured.py +0 -0
  577. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_stranded_halt_cause.py +0 -0
  578. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_surefire_run_level_signature.py +0 -0
  579. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_surefire_verdict_lines.py +0 -0
  580. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_template_closing_shapes.py +0 -0
  581. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_terminal_flip_ownership.py +0 -0
  582. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_terminal_flips.py +0 -0
  583. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_terminal_gate_message.py +0 -0
  584. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_tiered_verification_e2e.py +0 -0
  585. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_timeout_kill_portable.py +0 -0
  586. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage.py +0 -0
  587. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_apply.py +0 -0
  588. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_severity_classify.py +0 -0
  589. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_severity_end_to_end.py +0 -0
  590. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_severity_write.py +0 -0
  591. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_skill_contract.py +0 -0
  592. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_triage_skips_agent_escalations.py +0 -0
  593. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_tsc_verdict_lines.py +0 -0
  594. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_umbrella_version_resolution.py +0 -0
  595. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_unterminated_frontmatter.py +0 -0
  596. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_untracked_feature_folder.py +0 -0
  597. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_untracked_file_absorption.py +0 -0
  598. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_upgrade_integration.py +0 -0
  599. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_upgrade_merge_gate.py +0 -0
  600. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_validate_event.py +0 -0
  601. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_validate_event_driver_types.py +0 -0
  602. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_verdict_coupling.py +0 -0
  603. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_verify_empty_gate_set.py +0 -0
  604. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_version_consistency.py +0 -0
  605. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_version_skew.py +0 -0
  606. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_windows_gate_exec.py +0 -0
  607. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_worktree_restore.py +0 -0
  608. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_wu_execution_metadata.py +0 -0
  609. {specfuse_loop-0.22.1 → specfuse_loop-0.22.3}/tests/test_wu_template_renders_lintable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.22.1
3
+ Version: 0.22.3
4
4
  Summary: Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology.
5
5
  Author: Specfuse contributors
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specfuse-loop"
7
- version = "0.22.1"
7
+ version = "0.22.3"
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"
@@ -37,9 +37,13 @@ from typing import Callable, Optional
37
37
 
38
38
  from specfuse.agent.invoke import run_claude
39
39
  from specfuse.agent.triage_invoke import build_invocation, classify_severity
40
- from specfuse.loop.labels import read_severity_rubric
40
+ from specfuse.loop.labels import (
41
+ list_existing_labels,
42
+ read_severity_rubric,
43
+ severity_label_projection,
44
+ )
41
45
 
42
- from specfuse.loop import triage
46
+ from specfuse.loop import agent_policy, triage
43
47
  from specfuse.loop.build_provenance import warn_if_out_of_tree
44
48
 
45
49
  #: Stands in for T10's classification session (gate 3, walking skeleton).
@@ -50,7 +54,13 @@ def _default_runner(argv: list, check: bool = False):
50
54
  return subprocess.run(argv, check=check, capture_output=True, text=True)
51
55
 
52
56
 
53
- def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> list:
57
+ def apply_severity_backfill(
58
+ runner: Callable,
59
+ repo: str,
60
+ decisions: list,
61
+ *,
62
+ label_projection: "dict | None" = None,
63
+ ) -> list:
54
64
  """Record each decision in `decisions` against its GitHub issue, marker
55
65
  first, label second.
56
66
 
@@ -65,6 +75,13 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
65
75
  amended marker in place -- the marker is the authoritative record, the
66
76
  label a projection re-derived from it (`PLAN.md`'s "Record
67
77
  precedence").
78
+
79
+ `label_projection` maps a vocabulary value to the label this repository
80
+ spells it with (`labels.severity_label_projection`, #3353). The marker
81
+ always records the vocabulary value, so it stays comparable across
82
+ repositories; only the label takes the operator's own word. Absent or
83
+ missing a value, the vocabulary label is written -- the behaviour before
84
+ the projection existed.
68
85
  """
69
86
  results = []
70
87
  for decision in decisions:
@@ -88,6 +105,7 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
88
105
  "number": number,
89
106
  "severity": severity,
90
107
  "skipped": False,
108
+ "severity_source": decision.get("severity_source", "classifier"),
91
109
  }
92
110
 
93
111
  new_body = triage.amend_marker_severity(body, severity)
@@ -104,7 +122,19 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
104
122
  continue
105
123
  row["marker_written"] = True
106
124
 
107
- label = triage.severity_label_for(severity)
125
+ if decision.get("severity_source") == "label":
126
+ # Reconciled (#3360): the label IS the source of this severity, so
127
+ # it is already on the issue. Writing one would at best re-apply
128
+ # what is there and at worst add a second spelling beside the
129
+ # person's, which is the collision this path exists to avoid.
130
+ row["label_written"] = False
131
+ row["label_skipped_reason"] = "severity read from the issue's own label"
132
+ results.append(row)
133
+ continue
134
+
135
+ label = (label_projection or {}).get(
136
+ severity, triage.severity_label_for(severity)
137
+ )
108
138
  try:
109
139
  runner(
110
140
  ["gh", "issue", "edit", str(number), "--repo", repo, "--add-label", label],
@@ -156,7 +186,14 @@ def run_backfill(
156
186
  once `apply_severity_backfill` has run over it, that function's own
157
187
  `skipped`/`marker_written`/`label_written` fields.
158
188
  """
159
- rubric = read_severity_rubric(working_dir, runner=runner, repo=repo)
189
+ # One `gh label list` feeds both the rubric and the write-side
190
+ # projection (#3353): the once-per-run listing this run is asserted on.
191
+ existing, _listing_reason = list_existing_labels(
192
+ working_dir, runner=runner, repo=repo
193
+ )
194
+ rubric = read_severity_rubric(
195
+ working_dir, runner=runner, repo=repo, existing=existing
196
+ )
160
197
  if not rubric:
161
198
  return {
162
199
  "rubric": {},
@@ -180,6 +217,8 @@ def run_backfill(
180
217
  ),
181
218
  }
182
219
 
220
+ aliases = agent_policy.resolve_severity_aliases()
221
+
183
222
  rows = []
184
223
  decisions = []
185
224
  for issue in candidates:
@@ -187,6 +226,31 @@ def run_backfill(
187
226
  title = issue.get("title", "")
188
227
  body = issue.get("body") or ""
189
228
 
229
+ # The label is consulted BEFORE any classification session (#3360): an
230
+ # issue a person already labelled must have its marker written from
231
+ # that label, never from a fresh opinion about it. This also means a
232
+ # reconciled issue costs no `claude` invocation at all.
233
+ action, labelled_severity, reason = triage.severity_decision(
234
+ issue.get("labels"), aliases)
235
+
236
+ if action == triage.SEVERITY_SKIP:
237
+ rows.append({
238
+ "number": number, "classified": False, "severity": None,
239
+ "severity_source": "label", "skipped_reason": reason,
240
+ })
241
+ continue
242
+
243
+ if action == triage.SEVERITY_RECONCILE:
244
+ rows.append({
245
+ "number": number, "classified": True,
246
+ "severity": labelled_severity, "severity_source": "label",
247
+ })
248
+ decisions.append({
249
+ "number": number, "body": body,
250
+ "severity": labelled_severity, "severity_source": "label",
251
+ })
252
+ continue
253
+
190
254
  argv, prompt = build_invocation(
191
255
  number, title, body, repo, working_dir, model=model, effort=effort, rubric=rubric
192
256
  )
@@ -194,14 +258,24 @@ def run_backfill(
194
258
  severity = classify_severity(invoked.text, rubric)
195
259
 
196
260
  if severity is None:
197
- rows.append({"number": number, "classified": False, "severity": None})
261
+ rows.append({"number": number, "classified": False, "severity": None,
262
+ "severity_source": "classifier"})
198
263
  continue
199
264
 
200
- rows.append({"number": number, "classified": True, "severity": severity})
201
- decisions.append({"number": number, "body": body, "severity": severity})
265
+ rows.append({"number": number, "classified": True, "severity": severity,
266
+ "severity_source": "classifier"})
267
+ decisions.append({"number": number, "body": body, "severity": severity,
268
+ "severity_source": "classifier"})
202
269
 
203
270
  if apply and decisions:
204
- write_results = apply_severity_backfill(runner, repo, decisions)
271
+ # A property of the repository's label set, not of any one issue
272
+ # (#3353) -- derived from the listing already read above.
273
+ projection = severity_label_projection(
274
+ [item.get("name", "") for item in (existing or [])]
275
+ )
276
+ write_results = apply_severity_backfill(
277
+ runner, repo, decisions, label_projection=projection
278
+ )
205
279
  by_number = {result["number"]: result for result in write_results}
206
280
  for row in rows:
207
281
  result = by_number.get(row["number"])
@@ -238,7 +312,19 @@ def _print_run_report(report: dict) -> None:
238
312
  return
239
313
  for row in report["rows"]:
240
314
  if not row["classified"]:
241
- print(f"#{row['number']}: no usable classification, skipped")
315
+ # A refusal to overwrite a person is not a classification failure,
316
+ # and #3360's re-run condition is read off these lines: it asks for
317
+ # zero rows the run would contradict, which is unanswerable if both
318
+ # print the same sentence.
319
+ reason = row.get("skipped_reason")
320
+ if reason:
321
+ print(f"#{row['number']}: left alone — {reason}")
322
+ else:
323
+ print(f"#{row['number']}: no usable classification, skipped")
324
+ continue
325
+ if row.get("severity_source") == "label":
326
+ print(f"#{row['number']}: severity={row['severity']} "
327
+ f"(reconciled from its own label, not classified)")
242
328
  continue
243
329
  print(f"#{row['number']}: severity={row['severity']}")
244
330
 
@@ -363,10 +363,21 @@ def describe_budget_sources(**caps) -> str:
363
363
  that is not capped: the operator cannot tell which of the two they have.
364
364
  Each value is a `(value, source)` pair, source being `flag`, `policy` or
365
365
  `none`.
366
+
367
+ `max_items` carries an explicit `per run` (#3340). The policy key it comes
368
+ from is named `max_items_per_day` and is applied per run -- enforcing it
369
+ across a day needs state that survives a process. Printing `max_items=62
370
+ (policy)` against that key invites the reading the name suggests, and the
371
+ wrong reading is the unsafe one: a day of runs can exceed the number the
372
+ operator believes they set. Only this cap is annotated, because only this
373
+ one has a name promising something wider than it delivers; an unset cap
374
+ prints `unbounded` and needs no qualifier.
366
375
  """
367
376
  parts = []
368
377
  for name, (value, source) in sorted(caps.items()):
369
- parts.append(f"{name}={'unbounded' if value is None else value} ({source})")
378
+ rendered = "unbounded" if value is None else str(value)
379
+ qualifier = " per run" if name == "max_items" and value is not None else ""
380
+ parts.append(f"{name}={rendered} ({source}{qualifier})")
370
381
  return "budgets: " + ", ".join(parts)
371
382
 
372
383
 
@@ -0,0 +1 @@
1
+ 0.22.3
@@ -402,11 +402,75 @@ def _ensure_severity_labels(target, *, runner, repo=None) -> list:
402
402
  return failed
403
403
 
404
404
 
405
+ def severity_label_projection(label_names, *, aliases: Optional[dict] = None) -> dict:
406
+ """`{severity_value: label_name}` for the values a repository spells in its
407
+ own word (#3353), given the `severity:*` labels it defines.
408
+
409
+ `severity_aliases` (#3349) are honoured by the `min_severity` floor, which
410
+ reads an aliased label through `read_severity_label`. The classifier
411
+ FEAT-2026-0113 added answers in the published vocabulary instead, so the
412
+ write path projected `severity:high` at a repository whose issues all say
413
+ `severity:major` -- both spellings in circulation, and nothing reporting
414
+ the asymmetry.
415
+
416
+ This is the write-side half. The classifier is deliberately left answering
417
+ in the vocabulary so the marker's `severity=` field stays comparable across
418
+ repositories; only the *label* takes the repository's own word.
419
+
420
+ Pure over *label_names* rather than listing them itself, so the one caller
421
+ that needs both this and `read_severity_rubric` pays for a single
422
+ `gh label list` -- the once-per-run listing that run is asserted on.
423
+
424
+ Entries appear only where the projection actually differs, so an empty
425
+ result means "write the vocabulary label" and callers need no special case.
426
+ Two bounds keep it honest:
427
+
428
+ * **A label the repository does not define is never projected.** The
429
+ shipped `DEFAULT_SEVERITY_ALIASES` name words (`blocker`, `urgent`,
430
+ `trivial`) most repositories have never created; projecting one would
431
+ write a label that does not exist.
432
+ * **An in-vocabulary label always wins.** A repository defining both
433
+ `severity:high` and `severity:major` gets no entry for `high` -- the same
434
+ precedence `read_severity_rubric` applies when it builds the rubric.
435
+
436
+ Two aliases onto one value is the operator's own ambiguity. The lowest
437
+ label name sorts first, so a run is at least reproducible; defining the
438
+ vocabulary label is what removes the ambiguity.
439
+ """
440
+ prefix = agent_policy.SEVERITY_LABEL_PREFIX
441
+ try:
442
+ severity_names = sorted(
443
+ name for name in label_names if name.startswith(prefix)
444
+ )
445
+ except TypeError:
446
+ return {}
447
+
448
+ in_vocabulary = {
449
+ name[len(prefix):] for name in severity_names
450
+ if name[len(prefix):] in agent_policy.SEVERITY_VALUES
451
+ }
452
+
453
+ aliases = aliases if aliases is not None else agent_policy.resolve_severity_aliases()
454
+ projection: dict = {}
455
+ for name in severity_names:
456
+ word = name[len(prefix):]
457
+ if word in agent_policy.SEVERITY_VALUES:
458
+ continue
459
+ mapped = aliases.get(word.strip().lower())
460
+ if mapped not in agent_policy.SEVERITY_VALUES:
461
+ continue
462
+ if mapped in in_vocabulary or mapped in projection:
463
+ continue
464
+ projection[mapped] = name
465
+ return projection
466
+
467
+
405
468
  def read_severity_rubric(
406
469
  target: str | Path,
407
470
  *,
408
471
  runner: Optional[Callable] = None,
409
472
  repo: Optional[str] = None,
473
+ existing: Optional[list] = None,
410
474
  ) -> dict:
411
475
  """`{severity_value: description}` from the repo's own `severity:*` labels,
412
476
  or specfuse's `DEFAULT_SEVERITY_RUBRIC` when it has defined none (#3352).
@@ -428,7 +492,11 @@ def read_severity_rubric(
428
492
  """
429
493
  runner = runner if runner is not None else _default_runner
430
494
 
431
- existing, _reason = list_existing_labels(target, runner=runner, repo=repo)
495
+ # `existing` lets a caller that already listed the repository's labels pass
496
+ # them in rather than pay for a second `gh label list` (#3353). Absent, the
497
+ # listing happens here exactly as it always did.
498
+ if existing is None:
499
+ existing, _reason = list_existing_labels(target, runner=runner, repo=repo)
432
500
  if existing is None:
433
501
  return {}
434
502
 
@@ -125,7 +125,7 @@ SPECFUSE_DIR = Path(".specfuse")
125
125
  REPO_ROOT = SPECFUSE_DIR.parent
126
126
  FEATURES_DIR = SPECFUSE_DIR / "features"
127
127
  VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
128
- DRIVER_VERSION = "0.22.1"
128
+ DRIVER_VERSION = "0.22.3"
129
129
  # Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
130
130
  # version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
131
131
  # the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
@@ -2244,6 +2244,34 @@ def untracked_paths() -> set[str]:
2244
2244
  return {line.strip() for line in out.splitlines() if line.strip()}
2245
2245
 
2246
2246
 
2247
+ def ignored_paths() -> set[str]:
2248
+ """Repo-relative paths git reports as IGNORED, file by file.
2249
+
2250
+ Twin of :func:`untracked_paths`, snapshotted per-WU at dispatch so the
2251
+ attempt-failure path can tell "this ignored file was already here" from
2252
+ "this attempt produced it" (#3330). A `git reset --hard` restores tracked
2253
+ content and leaves ignored files in place, so without this the baseline
2254
+ attribution probe measures the failed attempt's own build output and
2255
+ reports the failure as pre-existing.
2256
+
2257
+ `--ignored` is deliberately paired with `ls-files` rather than read off
2258
+ `git status --porcelain --ignored`: status collapses a wholly-ignored
2259
+ directory to a single entry, so `!! out/` is byte-identical before and
2260
+ after an attempt adds files underneath it. That is precisely the reported
2261
+ case -- `out/` already existed and the attempt wrote 11 new files into it --
2262
+ and a snapshot diff over the status listing would have seen nothing.
2263
+
2264
+ Can be large on a repository carrying vendored dependency trees, so it is
2265
+ called only on the failure path that actually probes, where it is cheap
2266
+ beside the full gate-set run it precedes.
2267
+ """
2268
+ out = subprocess.run(
2269
+ ["git", "ls-files", "--others", "--ignored", "--exclude-standard"],
2270
+ capture_output=True, text=True, check=True,
2271
+ ).stdout
2272
+ return {line.strip() for line in out.splitlines() if line.strip()}
2273
+
2274
+
2247
2275
  def _tracked_dirty_paths() -> set[str]:
2248
2276
  """Paths with TRACKED, uncommitted changes (staged or unstaged), repo-relative.
2249
2277
 
@@ -3787,10 +3815,70 @@ def _clean_attempt_untracked(
3787
3815
  continue
3788
3816
 
3789
3817
 
3818
+ #: Ignored path prefixes the attempt-ignored clean never deletes. `work/` holds
3819
+ #: the per-attempt verify-output notes `persist_attempt_notes` writes (#168) --
3820
+ #: gitignored, and written AFTER the reset, so at the next attempt's reset they
3821
+ #: look exactly like the build output this clean exists to remove. The snapshot
3822
+ #: is taken once per WU rather than per attempt, so without this keep-list
3823
+ #: attempt 2 would delete attempt 1's evidence.
3824
+ IGNORED_CLEAN_KEEP_PREFIXES = ("work/", ".specfuse/")
3825
+
3826
+
3827
+ def _clean_attempt_ignored(
3828
+ ignored_before: "set[str] | frozenset[str]", events_path: Path,
3829
+ ) -> None:
3830
+ """Delete gitignored files that appeared since the *ignored_before* snapshot.
3831
+
3832
+ Defect 1 of #3330. `_clean_attempt_untracked` above deliberately leaves
3833
+ ignored paths alone, and that carve-out is load-bearing -- the driver's own
3834
+ gitignored `work/` notes have to survive the reset. But it also leaves the
3835
+ failed attempt's build output on disk, and the baseline attribution probe
3836
+ then runs against it: in the reported incident a unit's own generated files
3837
+ failed the probe, and the driver concluded the failure predated the feature
3838
+ and halted a green branch.
3839
+
3840
+ Only files that appeared SINCE the snapshot are removed, which is what makes
3841
+ this safe for the expensive case raised on #3371: a build cache present at
3842
+ dispatch is in the snapshot and is never touched. Only what this attempt
3843
+ produced goes.
3844
+
3845
+ Never deletes: paths in *ignored_before*, anything under
3846
+ `IGNORED_CLEAN_KEEP_PREFIXES`, *events_path*, or a per-criterion close-state
3847
+ file -- the same exclusions its untracked twin honours.
3848
+
3849
+ Best-effort: any git or filesystem error is swallowed, reverting to the old
3850
+ leftover behaviour rather than crashing the run.
3851
+ """
3852
+ try:
3853
+ root = Path(git("rev-parse", "--show-toplevel"))
3854
+ candidates = ignored_paths() - set(ignored_before)
3855
+ except (subprocess.CalledProcessError, OSError):
3856
+ return
3857
+ try:
3858
+ keep = events_path.resolve()
3859
+ except OSError:
3860
+ keep = None
3861
+ for rel in sorted(candidates):
3862
+ if any(rel.startswith(p) or f"/{p}" in rel
3863
+ for p in IGNORED_CLEAN_KEEP_PREFIXES):
3864
+ continue
3865
+ target = root / rel
3866
+ try:
3867
+ if keep is not None and target.resolve() == keep:
3868
+ continue
3869
+ if criteria_state.CRITERIA_FILENAME_RE.match(target.name):
3870
+ continue
3871
+ if target.is_file() or target.is_symlink():
3872
+ target.unlink()
3873
+ except OSError:
3874
+ continue
3875
+
3876
+
3790
3877
  def reset_preserving_events(
3791
3878
  head_before: str,
3792
3879
  events_path: Path,
3793
3880
  untracked_before: "set[str] | frozenset[str] | None" = None,
3881
+ ignored_before: "set[str] | frozenset[str] | None" = None,
3794
3882
  ) -> None:
3795
3883
  """`git reset --hard <head_before>` without losing events.jsonl content.
3796
3884
 
@@ -3828,6 +3916,12 @@ def reset_preserving_events(
3828
3916
  nothing untracked" and delete the entire untracked working tree; `None`
3829
3917
  means "no snapshot available, do not clean" and preserves the pre-#162
3830
3918
  behavior for callers that cannot supply one.
3919
+
3920
+ `ignored_before` (#3330 defect 1) does the same for GITIGNORED paths, and
3921
+ is supplied by the one call site whose reset is followed by the baseline
3922
+ attribution probe. Same inert-on-`None` contract. It is not passed on every
3923
+ reset: the listing it diffs walks ignored trees, which is cheap beside the
3924
+ gate-set run the probe performs and not worth paying where nothing probes.
3831
3925
  """
3832
3926
  saved = events_path.read_text() if events_path.is_file() else None
3833
3927
  git("reset", "--hard", head_before)
@@ -3835,6 +3929,8 @@ def reset_preserving_events(
3835
3929
  events_path.write_text(saved)
3836
3930
  if untracked_before is not None:
3837
3931
  _clean_attempt_untracked(untracked_before, events_path)
3932
+ if ignored_before is not None:
3933
+ _clean_attempt_ignored(ignored_before, events_path)
3838
3934
 
3839
3935
 
3840
3936
  def retain_tree_after_refusal(head_before: str, events_path: Path) -> str:
@@ -6485,6 +6581,7 @@ def baseline_evidence_diffstat(feat_fm: dict) -> "str | None":
6485
6581
  def format_preexisting_gate_failure(
6486
6582
  gate_number: int, failing_gates: list[dict], feat_fm: dict,
6487
6583
  done_unit_ids: "list[str] | None" = None,
6584
+ attributed_to: "str | None" = None,
6488
6585
  ) -> str:
6489
6586
  """Render the `preexisting_gate_failure` halt as a message a non-expert
6490
6587
  operator can act on without reading driver source (FEAT-2026-0051/T03).
@@ -6497,6 +6594,22 @@ def format_preexisting_gate_failure(
6497
6594
  defer the feature. There is no "proceed anyway" option in v1: the waiver
6498
6595
  is future work tracked as FEAT-2026-0052, never offered here as if it
6499
6596
  already existed.
6597
+
6598
+ `attributed_to` names the work unit whose failed attempt triggered the
6599
+ probe (#3330 defect 2). This message was written for a gate-entry probe,
6600
+ where "zero work units were dispatched" was true; FEAT-2026-0109/T01 then
6601
+ removed that probe, leaving attribution as the only path that renders it --
6602
+ and on that path a unit *was* dispatched and its failure is what caused the
6603
+ re-run. Left unsaid, every fresh-gate claim here is false exactly when the
6604
+ message is shown.
6605
+
6606
+ The proof is softened on every path (#3371). A `git diff --stat` compares
6607
+ TRACKED files, so it cannot see a check's gitignored inputs; `git reset
6608
+ --hard` restores tracked files and leaves ignored ones behind, which is how
6609
+ a failed attempt's own build output ends up failing the probe that is
6610
+ supposed to exonerate it. Stating "the failure predates this feature" from
6611
+ that diff reads as conclusive and cost one reported incident a bisect
6612
+ across three commits.
6500
6613
  """
6501
6614
  # A RESUMED gate -- one where work units already ran and committed -- has a
6502
6615
  # baseline that INCLUDES this feature's own landed work, so the fresh-gate
@@ -6505,8 +6618,17 @@ def format_preexisting_gate_failure(
6505
6618
  # that a previous unit of the same gate had added two commits earlier.
6506
6619
  done_unit_ids = list(done_unit_ids or [])
6507
6620
  resumed = bool(done_unit_ids)
6621
+ attributed = bool(attributed_to) and not resumed
6508
6622
 
6509
- if resumed:
6623
+ if attributed:
6624
+ lines = [
6625
+ f"Gate {gate_number} is blocked: the automated checks it depends "
6626
+ f"on failed on a probe the driver ran AFTER {attributed_to}'s "
6627
+ f"attempt failed and its changes were reset.",
6628
+ "",
6629
+ "Failing check(s) found by that probe:",
6630
+ ]
6631
+ elif resumed:
6510
6632
  lines = [
6511
6633
  f"Gate {gate_number} is blocked: the automated checks it depends on "
6512
6634
  f"are failing, and this gate has ALREADY LANDED WORK.",
@@ -6526,7 +6648,14 @@ def format_preexisting_gate_failure(
6526
6648
  f"(signature: {g['failure_signature']})"
6527
6649
  )
6528
6650
  lines.append("")
6529
- if resumed:
6651
+ if attributed:
6652
+ lines.append(
6653
+ f"{attributed_to} WAS dispatched and its attempt failed. The "
6654
+ f"driver then reset the tree and re-ran the checks to ask whether "
6655
+ f"the failure predated that attempt; the result above is what "
6656
+ f"that probe found."
6657
+ )
6658
+ elif resumed:
6530
6659
  lines.append(
6531
6660
  f"{len(done_unit_ids)} work unit(s) in this gate are already `done` "
6532
6661
  f"and committed, so the baseline these checks measured includes this "
@@ -6554,12 +6683,16 @@ def format_preexisting_gate_failure(
6554
6683
  )
6555
6684
  else:
6556
6685
  lines.append(
6557
- "Proof the failing check(s)' input files are unchanged vs "
6558
- "the integration branch (so the failure predates this "
6559
- "feature; the diff below is the feature's own plan/scaffold "
6560
- "files, not code the failing check(s) read):"
6686
+ "The failing check(s)' TRACKED input files are unchanged vs "
6687
+ "the integration branch (the diff below is the feature's own "
6688
+ "plan/scaffold files, not code the failing check(s) read):"
6561
6689
  )
6562
6690
  lines.append(diffstat)
6691
+ elif attributed:
6692
+ lines.append(
6693
+ "Base-tree comparison unavailable (no integration branch could "
6694
+ "be resolved, or the comparison itself failed)."
6695
+ )
6563
6696
  else:
6564
6697
  lines.append(
6565
6698
  "Base-tree comparison unavailable (no integration branch could "
@@ -6567,9 +6700,41 @@ def format_preexisting_gate_failure(
6567
6700
  "check(s) above were still measured before any work unit ran, "
6568
6701
  "so the conclusion stands even without this proof."
6569
6702
  )
6703
+ if not resumed:
6704
+ # The limit of the proof above, stated wherever it is offered (#3371).
6705
+ # A tracked diff is consistent with BOTH "pre-existing" and "left
6706
+ # behind by this run", so naming what it cannot see is the difference
6707
+ # between a reader checking `out/` first and bisecting the branch.
6708
+ lines.append("")
6709
+ lines.append(
6710
+ "What that comparison does NOT cover: untracked and gitignored "
6711
+ "paths. `git reset --hard` restores tracked files and leaves "
6712
+ "ignored ones in place, so a check that reads a build or output "
6713
+ "directory (`out/`, `target/`, generated-code trees, caches) can "
6714
+ "fail on state this feature's own failed attempt produced. The "
6715
+ "diff above cannot distinguish that from pre-existing breakage."
6716
+ )
6570
6717
  lines.append("")
6571
6718
  lines.append("What to do next:")
6572
- if resumed:
6719
+ if attributed:
6720
+ lines.append(
6721
+ " 1. Check for leftover untracked/ignored state from the reset "
6722
+ "attempt FIRST — it is the cheapest to rule out and the most "
6723
+ "likely cause. `git status --ignored --short` and `git clean -ndX` "
6724
+ "list what survived; if a listed path feeds a failing check, that "
6725
+ "is your answer."
6726
+ )
6727
+ lines.append(
6728
+ " 2. Only if the tree is clean of those is this pre-existing "
6729
+ "debt — confirm by running the failing check(s) on the "
6730
+ "integration branch in a fresh checkout before assuming it."
6731
+ )
6732
+ lines.append(
6733
+ f" 3. If it was leftover state, remove it and re-run; "
6734
+ f"{attributed_to} is back at its previous attempt count and will "
6735
+ f"be retried."
6736
+ )
6737
+ elif resumed:
6573
6738
  lines.append(
6574
6739
  " 1. Find which of this gate's landed work units introduced the "
6575
6740
  "failing check, and fix it ON THIS BRANCH. `git log -S<signature> "
@@ -10671,6 +10836,13 @@ def run(
10671
10836
  # (#150). Captured here, not inside squash_commit, because by
10672
10837
  # then the agent's own new files are indistinguishable from it.
10673
10838
  untracked_before = untracked_paths()
10839
+ # Ignored counterpart (#3330 defect 1), taken in the same
10840
+ # breath: only the attempt-failure reset that precedes the
10841
+ # baseline attribution probe consumes it, but it has to be
10842
+ # captured HERE, before the agent runs, or the attempt's own
10843
+ # build output is already indistinguishable from what was
10844
+ # on disk when it started.
10845
+ ignored_before = ignored_paths()
10674
10846
  _is_rearm = detect_rearm_dispatch(wu)
10675
10847
  if _is_rearm:
10676
10848
  fold_cumulative_on_rearm(wu, backend)
@@ -11646,9 +11818,15 @@ def run(
11646
11818
  untracked_before=untracked_before)
11647
11819
  print(" no FINDINGS metric — reset to base")
11648
11820
  else:
11821
+ # `ignored_before` is passed HERE and nowhere else: this
11822
+ # is the one reset whose tree is then measured by the
11823
+ # attribution probe below, so it is the one place where
11824
+ # the attempt's own gitignored output would be read as
11825
+ # pre-existing breakage (#3330 defect 1).
11649
11826
  reset_preserving_events(
11650
11827
  head_before, events_path,
11651
- untracked_before=untracked_before)
11828
+ untracked_before=untracked_before,
11829
+ ignored_before=ignored_before)
11652
11830
  # Retroactive baseline attribution (FEAT-2026-0109/T01):
11653
11831
  # decide, right after the reset above restores the
11654
11832
  # tree the unit was dispatched against, whether this
@@ -11685,7 +11863,8 @@ def run(
11685
11863
  gate.number, _attr_failing, feat_fm,
11686
11864
  done_unit_ids=[
11687
11865
  u.wu_id for u in units
11688
- if u.status == "done"]))
11866
+ if u.status == "done"],
11867
+ attributed_to=wu.wu_id))
11689
11868
  wu_events.append(build_event(
11690
11869
  "human_escalation", feature_id, {
11691
11870
  "reason": "preexisting_gate_failure",