specfuse-loop 0.22.1__tar.gz → 0.22.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 (605) hide show
  1. {specfuse_loop-0.22.1/specfuse_loop.egg-info → specfuse_loop-0.22.2}/PKG-INFO +1 -1
  2. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/pyproject.toml +1 -1
  3. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/severity_backfill.py +38 -5
  4. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/agent_policy.py +12 -1
  5. specfuse_loop-0.22.2/specfuse/loop/data/VERSION +1 -0
  6. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/labels.py +69 -1
  7. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/loop.py +1 -1
  8. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2/specfuse_loop.egg-info}/PKG-INFO +1 -1
  9. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/SOURCES.txt +4 -0
  10. specfuse_loop-0.22.2/tests/test_hygiene_boundary_guidance.py +88 -0
  11. specfuse_loop-0.22.2/tests/test_per_run_item_cap_disclosure.py +80 -0
  12. specfuse_loop-0.22.2/tests/test_plugin_prose_cross_refs.py +80 -0
  13. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_run.py +60 -0
  14. specfuse_loop-0.22.2/tests/test_severity_label_projection.py +138 -0
  15. specfuse_loop-0.22.1/specfuse/loop/data/VERSION +0 -1
  16. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/LICENSE +0 -0
  17. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/NOTICE +0 -0
  18. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/README.md +0 -0
  19. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/setup.cfg +0 -0
  20. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/__init__.py +0 -0
  21. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/budget.py +0 -0
  22. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/diagnose_invoke.py +0 -0
  23. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/drafting_answers.py +0 -0
  24. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/drafting_invoke.py +0 -0
  25. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/drafting_questions.py +0 -0
  26. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/driver_command.py +0 -0
  27. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/driver_invoke.py +0 -0
  28. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/invoke.py +0 -0
  29. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/monitoring_read.py +0 -0
  30. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/__init__.py +0 -0
  31. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/answers.py +0 -0
  32. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/bugs.py +0 -0
  33. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/feature.py +0 -0
  34. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/findings_autofix.py +0 -0
  35. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/findings_diagnose.py +0 -0
  36. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/providers/triage.py +0 -0
  37. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/queue_read.py +0 -0
  38. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/repo_detect.py +0 -0
  39. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/run.py +0 -0
  40. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/state.py +0 -0
  41. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/triage_invoke.py +0 -0
  42. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/agent/worktree.py +0 -0
  43. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/__init__.py +0 -0
  44. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/_filelock.py +0 -0
  45. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/_miniyaml.py +0 -0
  46. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/_wu_sections.py +0 -0
  47. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/adopt_feature.py +0 -0
  48. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/arm_eval.py +0 -0
  49. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/arm_sweep.py +0 -0
  50. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/arm_txn.py +0 -0
  51. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane.py +0 -0
  52. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane_run.py +0 -0
  53. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/bug_lane_state.py +0 -0
  54. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/build_provenance.py +0 -0
  55. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/caller_check.py +0 -0
  56. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/changelog.py +0 -0
  57. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/closing_requirements.py +0 -0
  58. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/cost.py +0 -0
  59. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/criteria_state.py +0 -0
  60. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  61. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  62. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  63. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  64. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  65. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  66. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/getting-started.md +0 -0
  67. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/glossary.md +0 -0
  68. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/methodology.md +0 -0
  69. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/docs/skills.md +0 -0
  70. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/gitignore.snippet +0 -0
  71. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  72. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  73. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/monitoring.yml.example +0 -0
  74. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/roadmap.template.md +0 -0
  75. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/close-discipline.md +0 -0
  76. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  77. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  78. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/human-output.md +0 -0
  79. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/never-touch.md +0 -0
  80. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  81. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  82. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/result-contract.md +0 -0
  83. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  84. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  85. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/rules-local/README.md +0 -0
  86. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/driver-event.schema.json +0 -0
  87. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/event.schema.json +0 -0
  88. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  89. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  90. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  91. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  92. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  93. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/GATE.template.md +0 -0
  94. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  95. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  96. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/templates/WU.template.md +0 -0
  97. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/verification.yml.example +0 -0
  98. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  99. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/decisions_format.py +0 -0
  100. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/driver_edit.py +0 -0
  101. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/escalation.py +0 -0
  102. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/events_stats.py +0 -0
  103. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gate_commands.py +0 -0
  104. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gate_eval.py +0 -0
  105. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gh_backend.py +0 -0
  106. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/gh_features.py +0 -0
  107. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/heartbeat.py +0 -0
  108. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/judge.py +0 -0
  109. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/learnings_distil.py +0 -0
  110. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/learnings_query.py +0 -0
  111. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_closing.py +0 -0
  112. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_monitoring.py +0 -0
  113. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_plan.py +0 -0
  114. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/lint_roadmap.py +0 -0
  115. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/notify.py +0 -0
  116. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/notify_escalation.py +0 -0
  117. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/notify_sla.py +0 -0
  118. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/plan_baseline.py +0 -0
  119. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/policy_proposals.py +0 -0
  120. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/policy_review.py +0 -0
  121. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/prerun.py +0 -0
  122. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/prerun_capture.py +0 -0
  123. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/promotion.py +0 -0
  124. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/rearm_migration.py +0 -0
  125. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/replay_spin.py +0 -0
  126. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/roadmap_archive.py +0 -0
  127. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/scaffold.py +0 -0
  128. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/triage.py +0 -0
  129. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/upgrade_merge_gate.py +0 -0
  130. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/loop/validate_event.py +0 -0
  131. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/__init__.py +0 -0
  132. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/adapters.py +0 -0
  133. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/artifact.py +0 -0
  134. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix.py +0 -0
  135. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_invoke.py +0 -0
  136. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_run.py +0 -0
  137. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/autofix_state.py +0 -0
  138. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/cli.py +0 -0
  139. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/diagnose_cli.py +0 -0
  140. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/diagnosis.py +0 -0
  141. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/fingerprint.py +0 -0
  142. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/issues.py +0 -0
  143. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/__init__.py +0 -0
  144. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/_azure_auth.py +0 -0
  145. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  146. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  147. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/redaction.py +0 -0
  148. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse/monitor/schedule.py +0 -0
  149. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  150. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/entry_points.txt +0 -0
  151. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/requires.txt +0 -0
  152. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/specfuse_loop.egg-info/top_level.txt +0 -0
  153. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_abandon_feature_skill.py +0 -0
  154. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_abandoned_work_reported.py +0 -0
  155. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_abandoned_wu_strands_gate.py +0 -0
  156. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_adopt_feature.py +0 -0
  157. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_budget.py +0 -0
  158. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_bug_selection_dedup.py +0 -0
  159. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_default_features_root.py +0 -0
  160. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_driver_command.py +0 -0
  161. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_driver_invoke.py +0 -0
  162. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_driver_restart.py +0 -0
  163. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_escalation_on_issue.py +0 -0
  164. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_escalation_run_state.py +0 -0
  165. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_findings_seam.py +0 -0
  166. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_invoke_timeout.py +0 -0
  167. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_invoke_usage.py +0 -0
  168. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_item_worktree.py +0 -0
  169. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_lock.py +0 -0
  170. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_key_ownership.py +0 -0
  171. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_queue.py +0 -0
  172. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_schema.py +0 -0
  173. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_policy_triage_dial.py +0 -0
  174. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_answers.py +0 -0
  175. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_bugs.py +0 -0
  176. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_feature.py +0 -0
  177. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_findings_autofix.py +0 -0
  178. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_findings_diagnose.py +0 -0
  179. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_isolation.py +0 -0
  180. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_provider_triage.py +0 -0
  181. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_queue_read.py +0 -0
  182. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_relays_driver_output.py +0 -0
  183. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_repo_detect.py +0 -0
  184. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_run.py +0 -0
  185. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_run_progress.py +0 -0
  186. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_seam.py +0 -0
  187. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_snapshot_refresh.py +0 -0
  188. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_agent_state.py +0 -0
  189. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_answer_escalation_skill.py +0 -0
  190. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_app_insights_adapters.py +0 -0
  191. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_archive_ref_reconciliation.py +0 -0
  192. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_eval.py +0 -0
  193. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_eval_lint_class.py +0 -0
  194. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_eval_wiring.py +0 -0
  195. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  196. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_sweep.py +0 -0
  197. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_sweep_gate.py +0 -0
  198. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_txn.py +0 -0
  199. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_arm_wiring.py +0 -0
  200. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_artifact_redaction.py +0 -0
  201. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attempt_note_untracked_files.py +0 -0
  202. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attempt_outcome_contract.py +0 -0
  203. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attempt_outcome_emission.py +0 -0
  204. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attention_nonwriting_guard.py +0 -0
  205. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attention_silence_section.py +0 -0
  206. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_attention_skill_structure.py +0 -0
  207. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autoclose_deferral_visibility.py +0 -0
  208. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autoclose_gate_note.py +0 -0
  209. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  210. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_invoke.py +0 -0
  211. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_live.py +0 -0
  212. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_predicate.py +0 -0
  213. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_run.py +0 -0
  214. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autofix_state.py +0 -0
  215. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync.py +0 -0
  216. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_consent.py +0 -0
  217. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_firstrun.py +0 -0
  218. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_no_cwd_leak.py +0 -0
  219. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_autosync_plugin.py +0 -0
  220. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_azure_credential_resolution.py +0 -0
  221. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_backend.py +0 -0
  222. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_escalation_message.py +0 -0
  223. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_persistence.py +0 -0
  224. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_probe.py +0 -0
  225. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_provenance.py +0 -0
  226. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_baseline_tree_hash_key.py +0 -0
  227. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bash_routing.py +0 -0
  228. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bats_suites_gated.py +0 -0
  229. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binary_verdict.py +0 -0
  230. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binding_block_advisory.py +0 -0
  231. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binding_block_allocation.py +0 -0
  232. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_binding_block_budget.py +0 -0
  233. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  234. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  235. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bookkeeping_no_verify.py +0 -0
  236. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  237. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_ci_pending.py +0 -0
  238. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_guardrails.py +0 -0
  239. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_label_nonfatal.py +0 -0
  240. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_labels_registered.py +0 -0
  241. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_min_severity.py +0 -0
  242. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_policy_contract.py +0 -0
  243. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_pr_number_carried.py +0 -0
  244. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  245. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_run.py +0 -0
  246. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_state.py +0 -0
  247. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  248. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_bug_lane_test_paths.py +0 -0
  249. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_build_provenance.py +0 -0
  250. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_caller_check_ratchet.py +0 -0
  251. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changed_file_test_selection.py +0 -0
  252. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_append_only.py +0 -0
  253. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_collection.py +0 -0
  254. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_release_wiring.py +0 -0
  255. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_changelog_schema.py +0 -0
  256. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ci_conclusion_wait.py +0 -0
  257. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ci_runs_every_gate.py +0 -0
  258. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_claude_resolution.py +0 -0
  259. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_close_b_pending_learnings.py +0 -0
  260. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_close_records_feature_oracle.py +0 -0
  261. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_close_wu_self_reference.py +0 -0
  262. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_deliverable_guard.py +0 -0
  263. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_guard_contracts.py +0 -0
  264. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_guard_prediction.py +0 -0
  265. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_learnings_destination.py +0 -0
  266. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_closing_requirements.py +0 -0
  267. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_coauthor_trailer_allowlist.py +0 -0
  268. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_conditional_reflection.py +0 -0
  269. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_console_encoding.py +0 -0
  270. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_convergent_iteration.py +0 -0
  271. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_convergent_wu_lint.py +0 -0
  272. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_correlation_id_override.py +0 -0
  273. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_correlation_id_two_surfaces.py +0 -0
  274. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_cost_lifetime.py +0 -0
  275. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_cost_tracking.py +0 -0
  276. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_criteria_per_gate_lint.py +0 -0
  277. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_criteria_state.py +0 -0
  278. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_criteria_worklist.py +0 -0
  279. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decision_citation_lint.py +0 -0
  280. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decision_followups_discharged.py +0 -0
  281. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decision_override_lint.py +0 -0
  282. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_decisions_format.py +0 -0
  283. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_default_severity_aliases.py +0 -0
  284. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_degraded_oracle.py +0 -0
  285. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_deliverable_missing_note.py +0 -0
  286. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_deliverable_presence_gate.py +0 -0
  287. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_agent_policy_review_mode.py +0 -0
  288. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_agent_policy_skill.py +0 -0
  289. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_monitoring_discovery.py +0 -0
  290. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_derive_monitoring_skill_registration.py +0 -0
  291. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_design_for_diagnosis_rule.py +0 -0
  292. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_deterministic_refusal_repeat.py +0 -0
  293. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnose_headless.py +0 -0
  294. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnose_issue_skill.py +0 -0
  295. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnosis.py +0 -0
  296. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_diagnosis_roundtrip_live.py +0 -0
  297. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_dispatch_skeleton.py +0 -0
  298. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_distilled_accept_step.py +0 -0
  299. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_doctor.py +0 -0
  300. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_doctor_command_preflight.py +0 -0
  301. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_done_feature_gates.py +0 -0
  302. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_draft_feature_answers_mode.py +0 -0
  303. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_answer_gate.py +0 -0
  304. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_invoke.py +0 -0
  305. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_question_issue.py +0 -0
  306. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_questions.py +0 -0
  307. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_drafting_reply_shape.py +0 -0
  308. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_edit_detection.py +0 -0
  309. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  310. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_integration.py +0 -0
  311. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_lock.py +0 -0
  312. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_module_surface.py +0 -0
  313. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_restart_halt_surfaced.py +0 -0
  314. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_restart_halt_wiring.py +0 -0
  315. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_restart_hold.py +0 -0
  316. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_staleness_gate_summary.py +0 -0
  317. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_driver_staleness_warning.py +0 -0
  318. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_dry_run_no_branch_mutation.py +0 -0
  319. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_duration_tracking.py +0 -0
  320. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_empty_files_escalation.py +0 -0
  321. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ensure_feature_branch.py +0 -0
  322. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ensure_feature_branch_base.py +0 -0
  323. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_assignee_policy.py +0 -0
  324. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_contract.py +0 -0
  325. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_crash_containment.py +0 -0
  326. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_dedup_search.py +0 -0
  327. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_escalation_emit.py +0 -0
  328. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_event_gate_full_envelope.py +0 -0
  329. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_events_redaction.py +0 -0
  330. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_events_stats.py +0 -0
  331. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_extra_gates.py +0 -0
  332. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_failure_artifact_model.py +0 -0
  333. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_failure_signature_fail_prefix.py +0 -0
  334. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_failure_signature_skips_log_noise.py +0 -0
  335. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_branch_auto_sync.py +0 -0
  336. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_branch_squash_merge.py +0 -0
  337. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_branch_stale_base.py +0 -0
  338. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_id_resolution.py +0 -0
  339. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_oracle_e2e.py +0 -0
  340. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  341. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_feature_review_accumulation.py +0 -0
  342. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_filelock_portable.py +0 -0
  343. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_files_changed_auto_repair.py +0 -0
  344. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_find_pr_no_search_index.py +0 -0
  345. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fingerprint.py +0 -0
  346. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fix_bug_diff_self_check.py +0 -0
  347. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fix_bug_headless.py +0 -0
  348. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fix_bug_reads_comments.py +0 -0
  349. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_fold_contract_documented.py +0 -0
  350. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_followups_artifact.py +0 -0
  351. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_force_full_close.py +0 -0
  352. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_frontmatter_scalar_quoting.py +0 -0
  353. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_broad_run.py +0 -0
  354. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_budget.py +0 -0
  355. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_budget_post_dispatch.py +0 -0
  356. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_commands_needs.py +0 -0
  357. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval.py +0 -0
  358. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_calibration.py +0 -0
  359. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  360. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_repo_flag.py +0 -0
  361. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_eval_terminal_wiring.py +0 -0
  362. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gate_report_verdict.py +0 -0
  363. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gh_backend.py +0 -0
  364. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gh_features.py +0 -0
  365. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_git_env_isolation.py +0 -0
  366. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_gitleaks_pinning.py +0 -0
  367. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_groom_backlog_skill.py +0 -0
  368. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_refusal_failure_class.py +0 -0
  369. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  370. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_repair_e2e.py +0 -0
  371. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_guard_repair_prompt.py +0 -0
  372. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_hashed_denylist.py +0 -0
  373. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_hashed_denylist_ci.py +0 -0
  374. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_heartbeat.py +0 -0
  375. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_heartbeat_adapter.py +0 -0
  376. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_human_output_rule.py +0 -0
  377. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_human_work_unit.py +0 -0
  378. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_hyphenated_gate_name.py +0 -0
  379. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_init_integration.py +0 -0
  380. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_installed_copy_driver_e2e.py +0 -0
  381. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_interpreter_normalization.py +0 -0
  382. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_close_path.py +0 -0
  383. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_cost.py +0 -0
  384. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_cost_fold_committed.py +0 -0
  385. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_module.py +0 -0
  386. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_path_registry.py +0 -0
  387. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_judge_terminal_flip_state.py +0 -0
  388. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_label_provisioning_runner_contract.py +0 -0
  389. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_label_registry.py +0 -0
  390. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_label_registry_covers_consumers.py +0 -0
  391. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_language_aware_narrow_selection.py +0 -0
  392. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lazy_baseline_e2e.py +0 -0
  393. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_leak_findings_redaction.py +0 -0
  394. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_leak_scan.py +0 -0
  395. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_leak_scan_content.py +0 -0
  396. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_curate_vendored_promote.py +0 -0
  397. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_distil_cli.py +0 -0
  398. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_query.py +0 -0
  399. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_staging.py +0 -0
  400. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_suggest_signature_filter.py +0 -0
  401. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_learnings_weights.py +0 -0
  402. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  403. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lifecycle_integration.py +0 -0
  404. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lifetime_accounting.py +0 -0
  405. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_ac_observable.py +0 -0
  406. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_bare_produces_path.py +0 -0
  407. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_boundary_consistency.py +0 -0
  408. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_boundary_extraction.py +0 -0
  409. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_close_intermediate.py +0 -0
  410. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  411. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_close_wu.py +0 -0
  412. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_closing.py +0 -0
  413. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_closing_criteria.py +0 -0
  414. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_closing_criteria_pristine.py +0 -0
  415. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_correlation_id.py +0 -0
  416. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  417. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  418. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_feature_oracle_declared.py +0 -0
  419. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_gate_proportionality.py +0 -0
  420. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_monitoring.py +0 -0
  421. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_oracle_env.py +0 -0
  422. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_contract_fields.py +0 -0
  423. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_errors.py +0 -0
  424. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_next_draft.py +0 -0
  425. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_relative_import.py +0 -0
  426. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_plan_verdict_exempt.py +0 -0
  427. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_planning_sections.py +0 -0
  428. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_produces_driver_helper.py +0 -0
  429. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_produces_satisfiability.py +0 -0
  430. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_roadmap.py +0 -0
  431. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_sections.py +0 -0
  432. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  433. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_lint_verification_reference.py +0 -0
  434. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_live_tests_opt_in.py +0 -0
  435. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_load_bearing_close_lint.py +0 -0
  436. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_attempt_evidence.py +0 -0
  437. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_auto_archive.py +0 -0
  438. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_caveman_preamble.py +0 -0
  439. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_close_intermediate.py +0 -0
  440. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_criteria_skeleton.py +0 -0
  441. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_criteria_survival.py +0 -0
  442. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_defaults_by_type.py +0 -0
  443. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_effort.py +0 -0
  444. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_failure_note_cap.py +0 -0
  445. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_files_changed_guard.py +0 -0
  446. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_gate_budget.py +0 -0
  447. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_gate_needs.py +0 -0
  448. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_model_alias.py +0 -0
  449. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_orchestration.py +0 -0
  450. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_post_pass_invariant.py +0 -0
  451. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_reset_preserving_events.py +0 -0
  452. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_smoke_runner.py +0 -0
  453. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_unsandboxed.py +0 -0
  454. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_worklist_injection.py +0 -0
  455. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_loop_zero_token_guard.py +0 -0
  456. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  457. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_marker_dual_shape.py +0 -0
  458. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_maven_failure_excerpt.py +0 -0
  459. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_max_attempts_configurable.py +0 -0
  460. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_max_open_prs.py +0 -0
  461. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_migrate_legacy.py +0 -0
  462. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_miniyaml_equivalence.py +0 -0
  463. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_miniyaml_negative.py +0 -0
  464. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitor_cli.py +0 -0
  465. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitor_issue_lifecycle.py +0 -0
  466. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitor_runner_surfaces.py +0 -0
  467. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  468. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_cron_dialect.py +0 -0
  469. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_example.py +0 -0
  470. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_fenced_blocks.py +0 -0
  471. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_monitoring_seed.py +0 -0
  472. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_narrow_fallback_warning.py +0 -0
  473. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_no_pytest_subprocess.py +0 -0
  474. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_notify.py +0 -0
  475. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_notify_escalation.py +0 -0
  476. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_notify_sla.py +0 -0
  477. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_operator_escalation_rule.py +0 -0
  478. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_oracle_set_declared.py +0 -0
  479. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_oracle_unrunnable_attribution.py +0 -0
  480. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pick_feature_skill.py +0 -0
  481. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pin_honesty_and_integrity.py +0 -0
  482. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pin_marker_not_inherited.py +0 -0
  483. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pin_marker_spawn_sites.py +0 -0
  484. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_plan_baseline.py +0 -0
  485. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_planned_cost_lint.py +0 -0
  486. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_planning_cost_floor.py +0 -0
  487. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_plugin_qualified_dispatch.py +0 -0
  488. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_policy_budgets_applied.py +0 -0
  489. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_policy_proposals.py +0 -0
  490. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_policy_review.py +0 -0
  491. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pr_checks_json_field.py +0 -0
  492. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pr_reference_in_title.py +0 -0
  493. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  494. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prep_halt_operator_surface.py +0 -0
  495. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prepare_feature.py +0 -0
  496. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prepare_scaffold_sync.py +0 -0
  497. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prerun_capture.py +0 -0
  498. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prerun_oracles.py +0 -0
  499. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_prerun_wiring.py +0 -0
  500. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produced_document_verdict_guard.py +0 -0
  501. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_bracket_literal.py +0 -0
  502. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_bracket_wildcard.py +0 -0
  503. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_field.py +0 -0
  504. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_in_diff.py +0 -0
  505. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_justification.py +0 -0
  506. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_recursive_glob.py +0 -0
  507. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_semantics_unified.py +0 -0
  508. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_produces_shape.py +0 -0
  509. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_progress_forward_note.py +0 -0
  510. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_progress_lines_end_to_end.py +0 -0
  511. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_promotion_ledger.py +0 -0
  512. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_provider_escalation_traces.py +0 -0
  513. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_provision_labels.py +0 -0
  514. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_queue_stalled_adapter.py +0 -0
  515. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  516. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rearm_fold_marker.py +0 -0
  517. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rearm_migration.py +0 -0
  518. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_red_baseline_never_reused.py +0 -0
  519. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_redaction_windows_home.py +0 -0
  520. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_reflection_required_signals.py +0 -0
  521. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_end_to_end.py +0 -0
  522. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_event_emission.py +0 -0
  523. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_note_collision.py +0 -0
  524. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_trigger.py +0 -0
  525. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replan_turn_contract.py +0 -0
  526. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_replay_spin.py +0 -0
  527. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_required_checks.py +0 -0
  528. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_resolve_base.py +0 -0
  529. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_restart_cap_counts_progress.py +0 -0
  530. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_result_block.py +0 -0
  531. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_result_block_audience.py +0 -0
  532. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_resume_command_build.py +0 -0
  533. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  534. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_add_skill.py +0 -0
  535. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_cli.py +0 -0
  536. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_shim.py +0 -0
  537. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_skill.py +0 -0
  538. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  539. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_link_gate.py +0 -0
  540. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_roadmap_row_parser.py +0 -0
  541. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_ruff_verdict_lines.py +0 -0
  542. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_rule_import_convergence.py +0 -0
  543. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_run_level_failure_breaker.py +0 -0
  544. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_data_in_sync.py +0 -0
  545. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_doc_hygiene.py +0 -0
  546. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_docs.py +0 -0
  547. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_init.py +0 -0
  548. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_label_provisioning.py +0 -0
  549. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_manifest.py +0 -0
  550. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_resources.py +0 -0
  551. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_seed_sanity.py +0 -0
  552. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_skew_halts.py +0 -0
  553. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_upgrade.py +0 -0
  554. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_scaffold_wiring.py +0 -0
  555. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_schedule_dialect.py +0 -0
  556. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_seed_documents_prerun_keys.py +0 -0
  557. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_service_bus_dlq_adapter.py +0 -0
  558. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_session_env_preflight.py +0 -0
  559. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_aliases.py +0 -0
  560. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_apply.py +0 -0
  561. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_end_to_end.py +0 -0
  562. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_limit.py +0 -0
  563. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_backfill_marker.py +0 -0
  564. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_severity_rubric.py +0 -0
  565. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_shipped_templates_parse.py +0 -0
  566. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_skill_discovery_links.py +0 -0
  567. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_skills_vendored_in_sync.py +0 -0
  568. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_spinning_rearm_gate.py +0 -0
  569. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_spinout_brief_end_to_end.py +0 -0
  570. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_spinout_brief_replan_option.py +0 -0
  571. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_squash_commit_hook_crash.py +0 -0
  572. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_stop_rationale_captured.py +0 -0
  573. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_stranded_halt_cause.py +0 -0
  574. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_surefire_run_level_signature.py +0 -0
  575. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_surefire_verdict_lines.py +0 -0
  576. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_template_closing_shapes.py +0 -0
  577. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_terminal_flip_ownership.py +0 -0
  578. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_terminal_flips.py +0 -0
  579. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_terminal_gate_message.py +0 -0
  580. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_tiered_verification_e2e.py +0 -0
  581. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_timeout_kill_portable.py +0 -0
  582. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage.py +0 -0
  583. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_apply.py +0 -0
  584. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_severity_classify.py +0 -0
  585. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_severity_end_to_end.py +0 -0
  586. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_severity_write.py +0 -0
  587. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_skill_contract.py +0 -0
  588. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_triage_skips_agent_escalations.py +0 -0
  589. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_tsc_verdict_lines.py +0 -0
  590. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_umbrella_version_resolution.py +0 -0
  591. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_unterminated_frontmatter.py +0 -0
  592. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_untracked_feature_folder.py +0 -0
  593. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_untracked_file_absorption.py +0 -0
  594. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_upgrade_integration.py +0 -0
  595. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_upgrade_merge_gate.py +0 -0
  596. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_validate_event.py +0 -0
  597. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_validate_event_driver_types.py +0 -0
  598. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_verdict_coupling.py +0 -0
  599. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_verify_empty_gate_set.py +0 -0
  600. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_version_consistency.py +0 -0
  601. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_version_skew.py +0 -0
  602. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_windows_gate_exec.py +0 -0
  603. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_worktree_restore.py +0 -0
  604. {specfuse_loop-0.22.1 → specfuse_loop-0.22.2}/tests/test_wu_execution_metadata.py +0 -0
  605. {specfuse_loop-0.22.1 → specfuse_loop-0.22.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.22.1
3
+ Version: 0.22.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.22.1"
7
+ version = "0.22.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"
@@ -37,7 +37,11 @@ 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
46
  from specfuse.loop import triage
43
47
  from specfuse.loop.build_provenance import warn_if_out_of_tree
@@ -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:
@@ -104,7 +121,9 @@ def apply_severity_backfill(runner: Callable, repo: str, decisions: list) -> lis
104
121
  continue
105
122
  row["marker_written"] = True
106
123
 
107
- label = triage.severity_label_for(severity)
124
+ label = (label_projection or {}).get(
125
+ severity, triage.severity_label_for(severity)
126
+ )
108
127
  try:
109
128
  runner(
110
129
  ["gh", "issue", "edit", str(number), "--repo", repo, "--add-label", label],
@@ -156,7 +175,14 @@ def run_backfill(
156
175
  once `apply_severity_backfill` has run over it, that function's own
157
176
  `skipped`/`marker_written`/`label_written` fields.
158
177
  """
159
- rubric = read_severity_rubric(working_dir, runner=runner, repo=repo)
178
+ # One `gh label list` feeds both the rubric and the write-side
179
+ # projection (#3353): the once-per-run listing this run is asserted on.
180
+ existing, _listing_reason = list_existing_labels(
181
+ working_dir, runner=runner, repo=repo
182
+ )
183
+ rubric = read_severity_rubric(
184
+ working_dir, runner=runner, repo=repo, existing=existing
185
+ )
160
186
  if not rubric:
161
187
  return {
162
188
  "rubric": {},
@@ -201,7 +227,14 @@ def run_backfill(
201
227
  decisions.append({"number": number, "body": body, "severity": severity})
202
228
 
203
229
  if apply and decisions:
204
- write_results = apply_severity_backfill(runner, repo, decisions)
230
+ # A property of the repository's label set, not of any one issue
231
+ # (#3353) -- derived from the listing already read above.
232
+ projection = severity_label_projection(
233
+ [item.get("name", "") for item in (existing or [])]
234
+ )
235
+ write_results = apply_severity_backfill(
236
+ runner, repo, decisions, label_projection=projection
237
+ )
205
238
  by_number = {result["number"]: result for result in write_results}
206
239
  for row in rows:
207
240
  result = by_number.get(row["number"])
@@ -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.2
@@ -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.2"
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.22.1
3
+ Version: 0.22.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
@@ -367,6 +367,7 @@ tests/test_heartbeat.py
367
367
  tests/test_heartbeat_adapter.py
368
368
  tests/test_human_output_rule.py
369
369
  tests/test_human_work_unit.py
370
+ tests/test_hygiene_boundary_guidance.py
370
371
  tests/test_hyphenated_gate_name.py
371
372
  tests/test_init_integration.py
372
373
  tests/test_installed_copy_driver_e2e.py
@@ -469,6 +470,7 @@ tests/test_notify_sla.py
469
470
  tests/test_operator_escalation_rule.py
470
471
  tests/test_oracle_set_declared.py
471
472
  tests/test_oracle_unrunnable_attribution.py
473
+ tests/test_per_run_item_cap_disclosure.py
472
474
  tests/test_pick_feature_skill.py
473
475
  tests/test_pin_honesty_and_integrity.py
474
476
  tests/test_pin_marker_not_inherited.py
@@ -476,6 +478,7 @@ tests/test_pin_marker_spawn_sites.py
476
478
  tests/test_plan_baseline.py
477
479
  tests/test_planned_cost_lint.py
478
480
  tests/test_planning_cost_floor.py
481
+ tests/test_plugin_prose_cross_refs.py
479
482
  tests/test_plugin_qualified_dispatch.py
480
483
  tests/test_policy_budgets_applied.py
481
484
  tests/test_policy_proposals.py
@@ -554,6 +557,7 @@ tests/test_severity_backfill_end_to_end.py
554
557
  tests/test_severity_backfill_limit.py
555
558
  tests/test_severity_backfill_marker.py
556
559
  tests/test_severity_backfill_run.py
560
+ tests/test_severity_label_projection.py
557
561
  tests/test_severity_rubric.py
558
562
  tests/test_shipped_templates_parse.py
559
563
  tests/test_skill_discovery_links.py
@@ -0,0 +1,88 @@
1
+ # Copyright 2026 Specfuse Contributors
2
+ # Licensed under the Apache License, Version 2.0. See LICENSE.
3
+ """The authoring rule teaches the reword the lint asks for (#3357).
4
+
5
+ `lint_plan` WARNs when a work unit declares a `produces:` path that also matches
6
+ one of its own Do-not-touch patterns, and since #3357 that warning names the
7
+ fix: reword the boundary to name the *surfaces* it protects rather than the file
8
+ this unit also produces.
9
+
10
+ The shape that trips it is not an edge case. A hygiene unit exists to make a
11
+ bounded fix inside a file a previous unit built, so it has to declare that file
12
+ in `produces:` *and* protect almost all of it — and §7 of the authoring skill,
13
+ which is where an author is told how to write a hygiene unit, said to name the
14
+ broken file in `produces:` without saying anything about how to word the
15
+ boundary. The author met the warning with no guidance in the rule that
16
+ generated the shape.
17
+
18
+ Worse than unhelpful: FEAT-2026-0113's `T08H` wrote "every other function in
19
+ `specfuse/loop/triage.py`", which read literally forbids editing the one
20
+ function that unit existed to fix. It passed only because the acceptance
21
+ criteria named the target unambiguously, so the boundary and the criteria
22
+ contradicted each other and only the criteria were load-bearing.
23
+
24
+ This guards the pairing rather than the prose: whatever wording either side
25
+ uses, the authoring rule must teach surface-naming, so the lint's recommended
26
+ fix and the rule that produces the shape cannot drift apart again.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import re
32
+ import unittest
33
+ from pathlib import Path
34
+
35
+ REPO_ROOT = Path(__file__).resolve().parent.parent
36
+ SKILL = REPO_ROOT / "plugins/specfuse/skills/authoring-work-units/SKILL.md"
37
+ MIRROR = REPO_ROOT / ".specfuse/skills/authoring-work-units/SKILL.md"
38
+ LINT = REPO_ROOT / "specfuse/loop/lint_plan.py"
39
+
40
+
41
+ def _section_7(text: str) -> str:
42
+ """§7, the hygiene-work-unit section, up to the next top-level heading."""
43
+ match = re.search(r"^## 7\. .*?$(.*?)(?=^## \d+\. )", text, re.S | re.M)
44
+ assert match, "authoring skill has no '## 7.' section"
45
+ return match.group(1)
46
+
47
+
48
+ class HygieneBoundaryGuidance(unittest.TestCase):
49
+
50
+ def test_the_lint_still_recommends_naming_surfaces(self):
51
+ # If this fails the lint changed its advice, and the rule below is
52
+ # now teaching the wrong reword rather than merely a stale one.
53
+ self.assertIn("surfaces it protects", LINT.read_text(encoding="utf-8"))
54
+
55
+ def test_section_7_tells_the_author_how_to_word_the_boundary(self):
56
+ body = _section_7(SKILL.read_text(encoding="utf-8"))
57
+
58
+ self.assertIn(
59
+ "surface", body.lower(),
60
+ "§7 tells an author to put the broken file in `produces:`, which is "
61
+ "exactly the shape that trips the Do-not-touch collision warning. "
62
+ "It must also tell them to word the boundary by naming the "
63
+ "surfaces it protects, or the rule generates a warning it gives no "
64
+ "way to answer (#3357).",
65
+ )
66
+
67
+ def test_section_7_warns_against_the_whole_file_wording(self):
68
+ body = _section_7(SKILL.read_text(encoding="utf-8")).lower()
69
+
70
+ self.assertTrue(
71
+ "every other function in" in body or "whole file" in body,
72
+ "§7 must show the wording that actually went wrong — a boundary "
73
+ "phrased over a whole file contradicts the criteria of a unit that "
74
+ "exists to edit one function in it (FEAT-2026-0113/T08H).",
75
+ )
76
+
77
+ def test_the_mirrored_copy_agrees(self):
78
+ # plugins/ is canonical; .specfuse/ is the synced scaffold copy.
79
+ self.assertEqual(
80
+ SKILL.read_text(encoding="utf-8"),
81
+ MIRROR.read_text(encoding="utf-8"),
82
+ "plugins/ and .specfuse/ copies of the authoring skill diverged — "
83
+ "run scripts/sync-scaffold.sh",
84
+ )
85
+
86
+
87
+ if __name__ == "__main__": # pragma: no cover
88
+ unittest.main()
@@ -0,0 +1,80 @@
1
+ # Copyright 2026 Specfuse Contributors
2
+ # Licensed under the Apache License, Version 2.0. See LICENSE.
3
+ """`max_items_per_day` is enforced per run, and now says so (#3340).
4
+
5
+ Most of #3340 landed: budgets resolve from the policy when no flag is given,
6
+ `max_open_prs` gates bug-lane dispatch, and the run opens with a line naming
7
+ each effective cap and its source.
8
+
9
+ One expectation was left. The policy key is named `max_items_per_day` and is
10
+ applied *per run* -- enforcing it across runs within a day needs state that
11
+ survives a process, which is a feature, not this fix. `resolve_max_items`
12
+ records that honestly in its docstring, but a docstring is not a surface an
13
+ operator reads. The issue offered the alternative explicitly: enforce it across
14
+ runs, **or** document it as per-run.
15
+
16
+ So the operator-facing surfaces carry it. A run that prints `max_items=62
17
+ (policy)` against a policy whose key says `per_day` invites exactly the wrong
18
+ reading -- and the wrong reading is the unsafe one, since a day of runs can
19
+ exceed the number the operator believes they set.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import unittest
25
+ from pathlib import Path
26
+
27
+ from specfuse.loop import agent_policy
28
+
29
+ REPO_ROOT = Path(__file__).resolve().parent.parent
30
+ SKILL = REPO_ROOT / "plugins/specfuse/skills/derive-agent-policy/SKILL.md"
31
+
32
+
33
+ class PerRunItemCapDisclosure(unittest.TestCase):
34
+
35
+ def test_the_run_line_says_the_item_cap_is_per_run(self):
36
+ line = agent_policy.describe_budget_sources(
37
+ max_items=(62, "policy"),
38
+ max_tokens=(2800000, "policy"),
39
+ max_minutes=(None, "none"),
40
+ )
41
+
42
+ self.assertIn("per run", line)
43
+
44
+ def test_a_flag_sourced_item_cap_says_it_too(self):
45
+ # The cap is per-run whoever set it; the policy key's name is what
46
+ # misleads, but a flag run is bounded the same way.
47
+ line = agent_policy.describe_budget_sources(max_items=(5, "flag"))
48
+
49
+ self.assertIn("per run", line)
50
+
51
+ def test_other_caps_are_not_annotated(self):
52
+ # Only the item cap has a name that promises something wider than it
53
+ # delivers. Annotating the rest would be noise.
54
+ line = agent_policy.describe_budget_sources(
55
+ max_tokens=(2800000, "policy"),
56
+ max_open_prs=(3, "policy"),
57
+ )
58
+
59
+ self.assertNotIn("per run", line)
60
+
61
+ def test_an_absent_item_cap_is_not_annotated(self):
62
+ line = agent_policy.describe_budget_sources(max_items=(None, "none"))
63
+
64
+ self.assertIn("unbounded", line)
65
+ self.assertNotIn("per run", line)
66
+
67
+ def test_the_drafting_skill_discloses_the_semantics(self):
68
+ body = SKILL.read_text(encoding="utf-8")
69
+
70
+ self.assertIn(
71
+ "per run", body,
72
+ "/derive-agent-policy drafts `max_items_per_day` into an "
73
+ "operator's policy file. It must say that the key is applied per "
74
+ "run, or the operator writes a number believing it bounds a day "
75
+ "(#3340).",
76
+ )
77
+
78
+
79
+ if __name__ == "__main__": # pragma: no cover
80
+ unittest.main()
@@ -0,0 +1,80 @@
1
+ #
2
+ # Copyright 2026 Specfuse Contributors
3
+ # Licensed under the Apache License, Version 2.0. See LICENSE.
4
+ #
5
+ """Published prose cross-references this project only (#3378).
6
+
7
+ Two surfaces are published content: everything under ``plugins/``, which ships
8
+ in a ``specfuse-loop`` release, is mirrored into the umbrella marketplace repo
9
+ and lands in every consumer's checkout when they install the plugin; and
10
+ ``CHANGELOG.md``, which ships in the pip package. Both are a far wider surface
11
+ than the commit bodies, PR bodies, roadmap prose and ``events.jsonl`` notes the
12
+ leak-scan discipline already guards.
13
+
14
+ The failure this guards against is #3378: ``fix-bug/SKILL.md`` cited a real
15
+ downstream consumer's ``org/repo#issue`` while explaining the ``Closes #<n>``
16
+ rule, and the 0.22.1 changelog entry describing that same fix cited it again.
17
+ Both shipped before anyone noticed.
18
+
19
+ `leak_scan` could not catch it and still cannot. A bare ``org/repo#n`` token is
20
+ indistinguishable from the many legitimate ``specfuse/loop#NNNN``
21
+ cross-references the same prose uses, and the denylist is the wrong instrument
22
+ here for a sharper reason: the consumer's name legitimately appears in
23
+ ``.specfuse/agent-policy.yml`` (it is the repo the bug lane is pointed at) and
24
+ in this repo's own tests. A denylist entry cannot tell published prose from
25
+ local configuration, so adding one would red the whole repository rather than
26
+ the one file that was actually wrong.
27
+
28
+ So the invariant is scoped to the published surfaces instead, and stated
29
+ positively: prose under ``plugins/`` and in ``CHANGELOG.md`` may cross-reference
30
+ this project's own repositories and nothing else. Any other ``org/repo#n`` is someone else's
31
+ identity riding out in our release.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import re
37
+ import unittest
38
+ from pathlib import Path
39
+
40
+ REPO_ROOT = Path(__file__).resolve().parent.parent
41
+ PLUGINS_DIR = REPO_ROOT / "plugins"
42
+ CHANGELOG = REPO_ROOT / "CHANGELOG.md"
43
+
44
+ # `owner/repo#123`. Deliberately broad on the owner/repo halves: the point is to
45
+ # catch an owner we have never seen, so the pattern cannot enumerate them.
46
+ CROSS_REF_RE = re.compile(r"\b([A-Za-z0-9][A-Za-z0-9._-]*)/([A-Za-z0-9._-]+)#\d+")
47
+
48
+ # The only owner published prose may name. This project's own repositories are
49
+ # the ones a reader of a shipped skill can be expected to follow.
50
+ ALLOWED_OWNERS = frozenset({"specfuse"})
51
+
52
+
53
+ class PluginProseCrossRefs(unittest.TestCase):
54
+
55
+ def test_published_prose_references_only_this_project(self):
56
+ offenders = []
57
+ published = sorted(PLUGINS_DIR.rglob("*.md")) + [CHANGELOG]
58
+ for path in published:
59
+ for lineno, line in enumerate(
60
+ path.read_text(encoding="utf-8").splitlines(), start=1
61
+ ):
62
+ for owner, repo in CROSS_REF_RE.findall(line):
63
+ if owner not in ALLOWED_OWNERS:
64
+ rel = path.relative_to(REPO_ROOT)
65
+ offenders.append(f"{rel}:{lineno}: {owner}/{repo}#…")
66
+
67
+ self.assertEqual(
68
+ offenders,
69
+ [],
70
+ "published plugin prose names a repository outside "
71
+ f"{sorted(ALLOWED_OWNERS)}. This content ships to every consumer, "
72
+ "so another party's org/repo must not ride along in it — describe "
73
+ "the repo instead ('a downstream consumer repo') and keep the "
74
+ "traceable reference on this project's own issue. Offenders:\n "
75
+ + "\n ".join(offenders),
76
+ )
77
+
78
+
79
+ if __name__ == "__main__": # pragma: no cover
80
+ unittest.main()
@@ -222,5 +222,65 @@ class BackfillRun(unittest.TestCase):
222
222
  self.assertEqual([], label_create_calls)
223
223
 
224
224
 
225
+ class AliasedSchemeWritesTheRepositorysOwnLabel(unittest.TestCase):
226
+ """End to end for #3353: a repository whose severity labels are its own
227
+ words gets those words written back, not the vocabulary spelling.
228
+
229
+ The unit tests for `severity_label_projection` prove the mapping; this
230
+ proves the wiring, which is where the defect actually lived -- the
231
+ projection existed nowhere and `apply_severity_backfill` wrote
232
+ `severity_label_for(value)` unconditionally.
233
+ """
234
+
235
+ ALIASED_LABELS = [
236
+ {"name": "severity:major", "description": "Wrong behavior."},
237
+ {"name": "severity:minor", "description": "Cosmetic."},
238
+ ]
239
+
240
+ def test_the_label_written_is_the_repositorys_word(self):
241
+ candidates = [_candidate(1)]
242
+ runner, calls = _make_runner(
243
+ candidates=candidates,
244
+ label_rows=self.ALIASED_LABELS,
245
+ claude_answers={1: _marker(1, "bug", "high", "high")},
246
+ )
247
+
248
+ report = run_backfill(runner, _REPO, apply=True)
249
+
250
+ self.assertEqual({"high", "low"}, set(report["rubric"]))
251
+
252
+ label_calls = [c for c in _issue_edit_calls(calls) if "--add-label" in c]
253
+ self.assertEqual(1, len(label_calls))
254
+ written = label_calls[0][label_calls[0].index("--add-label") + 1]
255
+ self.assertEqual("severity:major", written)
256
+
257
+ def test_the_marker_still_records_the_vocabulary_value(self):
258
+ candidates = [_candidate(1)]
259
+ runner, calls = _make_runner(
260
+ candidates=candidates,
261
+ label_rows=self.ALIASED_LABELS,
262
+ claude_answers={1: _marker(1, "bug", "high", "high")},
263
+ )
264
+
265
+ run_backfill(runner, _REPO, apply=True)
266
+
267
+ body_calls = [c for c in _issue_edit_calls(calls) if "--body" in c]
268
+ self.assertEqual(1, len(body_calls))
269
+ body = body_calls[0][body_calls[0].index("--body") + 1]
270
+ self.assertIn("severity=high", body)
271
+
272
+ def test_the_listing_is_still_read_exactly_once(self):
273
+ candidates = [_candidate(1)]
274
+ runner, calls = _make_runner(
275
+ candidates=candidates,
276
+ label_rows=self.ALIASED_LABELS,
277
+ claude_answers={1: _marker(1, "bug", "high", "high")},
278
+ )
279
+
280
+ run_backfill(runner, _REPO, apply=True)
281
+
282
+ self.assertEqual(1, len(_label_list_calls(calls)))
283
+
284
+
225
285
  if __name__ == "__main__":
226
286
  unittest.main()