specfuse-loop 0.30.0__tar.gz → 0.31.1__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 (709) hide show
  1. {specfuse_loop-0.30.0/specfuse_loop.egg-info → specfuse_loop-0.31.1}/PKG-INFO +4 -4
  2. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/README.md +3 -3
  3. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/pyproject.toml +1 -1
  4. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/__init__.py +1 -1
  5. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/agent_policy.py +12 -8
  6. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/arm_eval.py +112 -2
  7. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/changelog.py +5 -1
  8. specfuse_loop-0.31.1/specfuse/loop/data/VERSION +1 -0
  9. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +13 -6
  10. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/getting-started.md +8 -2
  11. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/glossary.md +3 -2
  12. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/methodology.md +34 -7
  13. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/roadmap.template.md +4 -1
  14. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/correlation-ids.md +14 -2
  15. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/schemas/driver-event.schema.json +4 -2
  16. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/schemas/event.schema.json +1 -1
  17. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +1 -1
  18. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +1 -1
  19. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/schemas/events/spec_validated.schema.json +1 -1
  20. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/gate_eval.py +11 -1
  21. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/gh_features.py +13 -3
  22. specfuse_loop-0.31.1/specfuse/loop/ids.py +890 -0
  23. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/layout.py +29 -4
  24. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/lint_plan.py +44 -3
  25. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/lint_roadmap.py +33 -4
  26. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/loop.py +196 -7
  27. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/promotion.py +2 -1
  28. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/upgrade_merge_gate.py +3 -1
  29. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1/specfuse_loop.egg-info}/PKG-INFO +4 -4
  30. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse_loop.egg-info/SOURCES.txt +15 -0
  31. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_policy_queue.py +4 -2
  32. specfuse_loop-0.31.1/tests/test_arm_eval_terminal.py +191 -0
  33. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_wiring.py +2 -2
  34. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_policy_contract.py +4 -2
  35. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_caller_check_ratchet.py +0 -1
  36. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_decision_followups_discharged.py +2 -2
  37. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_decision_override_lint.py +1 -1
  38. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_done_feature_gates.py +1 -1
  39. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_escalation_crash_containment.py +1 -1
  40. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_id_resolution.py +24 -18
  41. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_eval_calibration.py +7 -6
  42. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gh_features.py +6 -1
  43. specfuse_loop-0.31.1/tests/test_ids.py +128 -0
  44. specfuse_loop-0.31.1/tests/test_ids_aliases.py +127 -0
  45. specfuse_loop-0.31.1/tests/test_ids_call_sites.py +151 -0
  46. specfuse_loop-0.31.1/tests/test_ids_migrate.py +139 -0
  47. specfuse_loop-0.31.1/tests/test_ids_migrate_all.py +244 -0
  48. specfuse_loop-0.31.1/tests/test_judge_editing_terminal_docs.py +67 -0
  49. specfuse_loop-0.31.1/tests/test_keyed_feature_ids_docs.py +44 -0
  50. specfuse_loop-0.31.1/tests/test_keyed_feature_ids_end_to_end.py +297 -0
  51. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_layout_no_layout_goldens.py +2 -1
  52. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_close_wu.py +1 -1
  53. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_correlation_id.py +13 -11
  54. specfuse_loop-0.31.1/tests/test_lint_judge_edit_acknowledged.py +82 -0
  55. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_plan_errors.py +1 -1
  56. specfuse_loop-0.31.1/tests/test_lint_roadmap_unkeyed_rows.py +55 -0
  57. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_sections.py +1 -1
  58. specfuse_loop-0.31.1/tests/test_migrate_all_repo_stays_valid.py +337 -0
  59. specfuse_loop-0.31.1/tests/test_migrate_all_scales.py +56 -0
  60. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_miniyaml_equivalence.py +1 -1
  61. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replan_turn_contract.py +1 -1
  62. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_add_skill.py +28 -3
  63. specfuse_loop-0.31.1/tests/test_terminal_judge_editing_end_to_end.py +338 -0
  64. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_unterminated_frontmatter.py +1 -1
  65. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_validate_event.py +17 -5
  66. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_validate_event_driver_types.py +1 -1
  67. specfuse_loop-0.31.1/tests/test_validate_feature_dirs_only.py +37 -0
  68. specfuse_loop-0.30.0/specfuse/loop/data/VERSION +0 -1
  69. specfuse_loop-0.30.0/tests/test_judge_editing_terminal_docs.py +0 -58
  70. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/LICENSE +0 -0
  71. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/NOTICE +0 -0
  72. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/setup.cfg +0 -0
  73. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/budget.py +0 -0
  74. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/diagnose_invoke.py +0 -0
  75. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/drafting_answers.py +0 -0
  76. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/drafting_invoke.py +0 -0
  77. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/drafting_questions.py +0 -0
  78. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/driver_command.py +0 -0
  79. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/driver_invoke.py +0 -0
  80. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/invoke.py +0 -0
  81. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/monitoring_read.py +0 -0
  82. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/__init__.py +0 -0
  83. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/answers.py +0 -0
  84. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/bugs.py +0 -0
  85. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/feature.py +0 -0
  86. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/findings_autofix.py +0 -0
  87. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/findings_diagnose.py +0 -0
  88. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/providers/triage.py +0 -0
  89. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/queue_read.py +0 -0
  90. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/repo_detect.py +0 -0
  91. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/run.py +0 -0
  92. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/severity_backfill.py +0 -0
  93. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/state.py +0 -0
  94. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/triage_invoke.py +0 -0
  95. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/agent/worktree.py +0 -0
  96. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/__init__.py +0 -0
  97. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/_filelock.py +0 -0
  98. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/_miniyaml.py +0 -0
  99. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/_wu_sections.py +0 -0
  100. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/adopt_feature.py +0 -0
  101. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/arm_sweep.py +0 -0
  102. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/arm_txn.py +0 -0
  103. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/bug_lane.py +0 -0
  104. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/bug_lane_run.py +0 -0
  105. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/bug_lane_state.py +0 -0
  106. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/build_provenance.py +0 -0
  107. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/caller_check.py +0 -0
  108. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/closing_requirements.py +0 -0
  109. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/cost.py +0 -0
  110. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/criteria_state.py +0 -0
  111. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  112. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  113. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  114. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  115. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/monitoring-runners.md +0 -0
  116. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/monitoring-schema.md +0 -0
  117. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/monitoring-to-fix.md +0 -0
  118. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  119. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/flows.html +0 -0
  120. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/lifecycles.md +0 -0
  121. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/docs/skills.md +0 -0
  122. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/gitignore.snippet +0 -0
  123. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  124. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  125. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/monitoring.yml.example +0 -0
  126. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/close-discipline.md +0 -0
  127. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  128. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/human-output.md +0 -0
  129. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/never-touch.md +0 -0
  130. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/operator-escalation.md +0 -0
  131. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  132. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/result-contract.md +0 -0
  133. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  134. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules/verification-discipline.md +0 -0
  135. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/rules-local/README.md +0 -0
  136. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  137. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  138. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/templates/GATE.template.md +0 -0
  139. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  140. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  141. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/templates/WU.template.md +0 -0
  142. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/verification.yml.example +0 -0
  143. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  144. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/decisions_format.py +0 -0
  145. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/driver_edit.py +0 -0
  146. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/escalation.py +0 -0
  147. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/events_stats.py +0 -0
  148. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/gate_commands.py +0 -0
  149. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/gen_contract.py +0 -0
  150. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/gh_backend.py +0 -0
  151. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/heartbeat.py +0 -0
  152. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/judge.py +0 -0
  153. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/labels.py +0 -0
  154. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/learnings_distil.py +0 -0
  155. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/learnings_query.py +0 -0
  156. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/lint_closing.py +0 -0
  157. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/lint_monitoring.py +0 -0
  158. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/notify.py +0 -0
  159. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/notify_escalation.py +0 -0
  160. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/notify_sla.py +0 -0
  161. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/plan_baseline.py +0 -0
  162. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/policy_proposals.py +0 -0
  163. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/policy_review.py +0 -0
  164. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/prerun.py +0 -0
  165. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/prerun_capture.py +0 -0
  166. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/rearm_migration.py +0 -0
  167. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/red_on_base.py +0 -0
  168. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/replay_spin.py +0 -0
  169. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/roadmap_archive.py +0 -0
  170. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/scaffold.py +0 -0
  171. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/triage.py +0 -0
  172. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/loop/validate_event.py +0 -0
  173. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/__init__.py +0 -0
  174. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/adapters.py +0 -0
  175. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/artifact.py +0 -0
  176. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/autofix.py +0 -0
  177. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/autofix_invoke.py +0 -0
  178. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/autofix_run.py +0 -0
  179. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/autofix_state.py +0 -0
  180. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/cli.py +0 -0
  181. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/diagnose_cli.py +0 -0
  182. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/diagnosis.py +0 -0
  183. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/durations.py +0 -0
  184. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/fingerprint.py +0 -0
  185. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/issues.py +0 -0
  186. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/providers/__init__.py +0 -0
  187. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/providers/_azure_auth.py +0 -0
  188. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  189. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  190. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/redaction.py +0 -0
  191. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse/monitor/schedule.py +0 -0
  192. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  193. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse_loop.egg-info/entry_points.txt +0 -0
  194. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse_loop.egg-info/requires.txt +0 -0
  195. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/specfuse_loop.egg-info/top_level.txt +0 -0
  196. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_abandon_feature_skill.py +0 -0
  197. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_abandoned_work_reported.py +0 -0
  198. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_abandoned_wu_strands_gate.py +0 -0
  199. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_adopt_feature.py +0 -0
  200. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_budget.py +0 -0
  201. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_bug_selection_dedup.py +0 -0
  202. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_default_features_root.py +0 -0
  203. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_driver_command.py +0 -0
  204. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_driver_invoke.py +0 -0
  205. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_driver_restart.py +0 -0
  206. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_escalation_on_issue.py +0 -0
  207. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_escalation_run_state.py +0 -0
  208. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_findings_seam.py +0 -0
  209. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_invoke_timeout.py +0 -0
  210. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_invoke_usage.py +0 -0
  211. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_item_worktree.py +0 -0
  212. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_lock.py +0 -0
  213. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_policy_key_ownership.py +0 -0
  214. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_policy_schema.py +0 -0
  215. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_policy_triage_dial.py +0 -0
  216. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_answers.py +0 -0
  217. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_bugs.py +0 -0
  218. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_feature.py +0 -0
  219. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_findings_autofix.py +0 -0
  220. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_findings_diagnose.py +0 -0
  221. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_isolation.py +0 -0
  222. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_provider_triage.py +0 -0
  223. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_queue_read.py +0 -0
  224. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_relays_driver_output.py +0 -0
  225. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_repo_detect.py +0 -0
  226. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_run.py +0 -0
  227. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_run_progress.py +0 -0
  228. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_seam.py +0 -0
  229. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_snapshot_refresh.py +0 -0
  230. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_agent_state.py +0 -0
  231. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_answer_escalation_skill.py +0 -0
  232. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_app_insights_adapters.py +0 -0
  233. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_app_insights_harvest_window.py +0 -0
  234. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_app_insights_query_schema_check.py +0 -0
  235. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_app_insights_workspace_schema.py +0 -0
  236. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_archive_ref_reconciliation.py +0 -0
  237. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_eval.py +0 -0
  238. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_eval_lint_class.py +0 -0
  239. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_eval_wiring.py +0 -0
  240. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  241. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_sweep.py +0 -0
  242. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_sweep_gate.py +0 -0
  243. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_arm_txn.py +0 -0
  244. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_artifact_redaction.py +0 -0
  245. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attempt_note_untracked_files.py +0 -0
  246. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attempt_outcome_contract.py +0 -0
  247. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attempt_outcome_emission.py +0 -0
  248. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attention_nonwriting_guard.py +0 -0
  249. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attention_silence_section.py +0 -0
  250. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attention_skill_structure.py +0 -0
  251. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_attribution_halt_claims.py +0 -0
  252. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autoclose_deferral_visibility.py +0 -0
  253. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autoclose_gate_note.py +0 -0
  254. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  255. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autofix_invoke.py +0 -0
  256. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autofix_live.py +0 -0
  257. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autofix_predicate.py +0 -0
  258. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autofix_run.py +0 -0
  259. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autofix_state.py +0 -0
  260. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autosync.py +0 -0
  261. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autosync_consent.py +0 -0
  262. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autosync_firstrun.py +0 -0
  263. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autosync_no_cwd_leak.py +0 -0
  264. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_autosync_plugin.py +0 -0
  265. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_azure_credential_resolution.py +0 -0
  266. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_backend.py +0 -0
  267. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_baseline_escalation_message.py +0 -0
  268. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_baseline_persistence.py +0 -0
  269. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_baseline_probe.py +0 -0
  270. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_baseline_provenance.py +0 -0
  271. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_baseline_tree_hash_key.py +0 -0
  272. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bash_routing.py +0 -0
  273. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bats_suites_gated.py +0 -0
  274. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_binary_verdict.py +0 -0
  275. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_binding_block_advisory.py +0 -0
  276. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_binding_block_allocation.py +0 -0
  277. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_binding_block_budget.py +0 -0
  278. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_blind_run_is_not_drained.py +0 -0
  279. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_block_feature_layout.py +0 -0
  280. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  281. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  282. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bookkeeping_no_verify.py +0 -0
  283. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_brief_covers_every_unit_escalation.py +0 -0
  284. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_ci_pending.py +0 -0
  285. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_guardrails.py +0 -0
  286. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_label_nonfatal.py +0 -0
  287. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_labels_registered.py +0 -0
  288. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_min_severity.py +0 -0
  289. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_no_checks_pending.py +0 -0
  290. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_pr_number_carried.py +0 -0
  291. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  292. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_run.py +0 -0
  293. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_spend.py +0 -0
  294. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_state.py +0 -0
  295. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  296. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_superseded.py +0 -0
  297. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_bug_lane_test_paths.py +0 -0
  298. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_build_provenance.py +0 -0
  299. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_carried_green_invalidated_by_diff.py +0 -0
  300. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_carry_accounting_close_and_judge.py +0 -0
  301. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_changed_file_test_selection.py +0 -0
  302. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_changelog_append_only.py +0 -0
  303. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_changelog_collection.py +0 -0
  304. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_changelog_release_wiring.py +0 -0
  305. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_changelog_schema.py +0 -0
  306. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_ci_conclusion_wait.py +0 -0
  307. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_ci_runs_every_gate.py +0 -0
  308. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_claude_resolution.py +0 -0
  309. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_close_b_pending_learnings.py +0 -0
  310. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_close_records_feature_oracle.py +0 -0
  311. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_close_wu_self_reference.py +0 -0
  312. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closed_escalation_labels.py +0 -0
  313. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closing_deliverable_guard.py +0 -0
  314. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closing_guard_contracts.py +0 -0
  315. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closing_guard_prediction.py +0 -0
  316. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closing_learnings_destination.py +0 -0
  317. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closing_requirements.py +0 -0
  318. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_closing_sessions_on_sonnet.py +0 -0
  319. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_coauthor_trailer_allowlist.py +0 -0
  320. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_conditional_reflection.py +0 -0
  321. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_console_encoding.py +0 -0
  322. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_convergent_iteration.py +0 -0
  323. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_convergent_wu_lint.py +0 -0
  324. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_correlation_id_override.py +0 -0
  325. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_correlation_id_two_surfaces.py +0 -0
  326. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_cost_lifetime.py +0 -0
  327. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_cost_tracking.py +0 -0
  328. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_criteria_per_gate_lint.py +0 -0
  329. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_criteria_state.py +0 -0
  330. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_criteria_worklist.py +0 -0
  331. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_decision_citation_lint.py +0 -0
  332. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_decisions_format.py +0 -0
  333. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_default_severity_aliases.py +0 -0
  334. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_degraded_oracle.py +0 -0
  335. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_deliverable_missing_note.py +0 -0
  336. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_deliverable_presence_gate.py +0 -0
  337. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_derive_agent_policy_review_mode.py +0 -0
  338. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_derive_agent_policy_skill.py +0 -0
  339. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_derive_monitoring_discovery.py +0 -0
  340. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_derive_monitoring_skill_registration.py +0 -0
  341. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_design_for_diagnosis_rule.py +0 -0
  342. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_deterministic_refusal_repeat.py +0 -0
  343. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnose_fenced_analysis.py +0 -0
  344. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnose_headless.py +0 -0
  345. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnose_issue_skill.py +0 -0
  346. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnosis.py +0 -0
  347. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnosis_roundtrip_live.py +0 -0
  348. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnosis_severity_end_to_end.py +0 -0
  349. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_diagnosis_severity_rubric.py +0 -0
  350. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_dispatch_model_from_fix_scope.py +0 -0
  351. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_dispatch_preamble_rules_pointer.py +0 -0
  352. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_dispatch_skeleton.py +0 -0
  353. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_dispatch_skills_index.py +0 -0
  354. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_distilled_accept_step.py +0 -0
  355. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_doctor.py +0 -0
  356. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_doctor_command_preflight.py +0 -0
  357. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_draft_feature_answers_mode.py +0 -0
  358. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_drafting_answer_gate.py +0 -0
  359. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_drafting_invoke.py +0 -0
  360. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_drafting_question_issue.py +0 -0
  361. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_drafting_questions.py +0 -0
  362. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_drafting_reply_shape.py +0 -0
  363. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_edit_detection.py +0 -0
  364. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  365. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_integration.py +0 -0
  366. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_lock.py +0 -0
  367. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_module_surface.py +0 -0
  368. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_restart_halt_surfaced.py +0 -0
  369. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_restart_halt_wiring.py +0 -0
  370. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_restart_hold.py +0 -0
  371. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_staleness_gate_summary.py +0 -0
  372. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_driver_staleness_warning.py +0 -0
  373. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_dry_run_no_branch_mutation.py +0 -0
  374. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_duration_tracking.py +0 -0
  375. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_empty_files_escalation.py +0 -0
  376. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_ensure_feature_branch.py +0 -0
  377. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_ensure_feature_branch_base.py +0 -0
  378. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_escalation_assignee_policy.py +0 -0
  379. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_escalation_contract.py +0 -0
  380. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_escalation_dedup_search.py +0 -0
  381. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_escalation_emit.py +0 -0
  382. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_event_gate_full_envelope.py +0 -0
  383. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_events_redaction.py +0 -0
  384. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_events_stats.py +0 -0
  385. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_extra_gates.py +0 -0
  386. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_failure_artifact_model.py +0 -0
  387. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_failure_signature_fail_prefix.py +0 -0
  388. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_failure_signature_names_the_test.py +0 -0
  389. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_failure_signature_skips_log_noise.py +0 -0
  390. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_branch_auto_sync.py +0 -0
  391. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_branch_squash_merge.py +0 -0
  392. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_branch_stale_base.py +0 -0
  393. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_oracle_e2e.py +0 -0
  394. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  395. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_feature_review_accumulation.py +0 -0
  396. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_filelock_portable.py +0 -0
  397. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_files_changed_auto_repair.py +0 -0
  398. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_find_pr_no_search_index.py +0 -0
  399. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fingerprint.py +0 -0
  400. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_bug_cwd_worktree.py +0 -0
  401. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_bug_diff_self_check.py +0 -0
  402. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_bug_headless.py +0 -0
  403. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_bug_reads_comments.py +0 -0
  404. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_unit_insertion_bookkeeping.py +0 -0
  405. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_unit_insertion_e2e.py +0 -0
  406. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fix_unit_insertion_refused.py +0 -0
  407. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_floor_skip_summary.py +0 -0
  408. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_flows_page.py +0 -0
  409. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_fold_contract_documented.py +0 -0
  410. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_followups_artifact.py +0 -0
  411. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_force_full_close.py +0 -0
  412. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_frontmatter_scalar_quoting.py +0 -0
  413. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_broad_run.py +0 -0
  414. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_budget.py +0 -0
  415. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_budget_post_dispatch.py +0 -0
  416. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_commands_needs.py +0 -0
  417. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_eval.py +0 -0
  418. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  419. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_eval_repo_flag.py +0 -0
  420. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_eval_terminal_wiring.py +0 -0
  421. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_log_size_budget.py +0 -0
  422. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gate_report_verdict.py +0 -0
  423. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gen_contract.py +0 -0
  424. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gh_backend.py +0 -0
  425. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_git_env_isolation.py +0 -0
  426. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_gitleaks_pinning.py +0 -0
  427. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_groom_backlog_skill.py +0 -0
  428. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_guard_refusal_failure_class.py +0 -0
  429. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  430. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_guard_repair_e2e.py +0 -0
  431. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_guard_repair_prompt.py +0 -0
  432. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_hashed_denylist.py +0 -0
  433. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_hashed_denylist_ci.py +0 -0
  434. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_headless_no_background_tasks.py +0 -0
  435. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_heartbeat.py +0 -0
  436. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_heartbeat_adapter.py +0 -0
  437. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_heartbeat_emission_contract.py +0 -0
  438. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_http_status_regression_adapter.py +0 -0
  439. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_http_status_regression_end_to_end.py +0 -0
  440. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_human_output_rule.py +0 -0
  441. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_human_work_unit.py +0 -0
  442. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_hygiene_boundary_guidance.py +0 -0
  443. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_hyphenated_gate_name.py +0 -0
  444. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_ignored_cleanup_before_probe.py +0 -0
  445. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_init_integration.py +0 -0
  446. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_installed_copy_driver_e2e.py +0 -0
  447. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_interpreter_normalization.py +0 -0
  448. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_close_path.py +0 -0
  449. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_cost.py +0 -0
  450. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_cost_fold_committed.py +0 -0
  451. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_module.py +0 -0
  452. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_path_registry.py +0 -0
  453. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_sees_dropped_produces.py +0 -0
  454. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judge_terminal_flip_state.py +0 -0
  455. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_judged_event_points_at_findings.py +0 -0
  456. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_label_description_length.py +0 -0
  457. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_label_provisioning_runner_contract.py +0 -0
  458. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_label_registry.py +0 -0
  459. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_label_registry_covers_consumers.py +0 -0
  460. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_language_aware_narrow_selection.py +0 -0
  461. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_layout.py +0 -0
  462. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_layout_end_to_end.py +0 -0
  463. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_layout_link.py +0 -0
  464. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_layout_validation.py +0 -0
  465. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lazy_baseline_e2e.py +0 -0
  466. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_leak_findings_redaction.py +0 -0
  467. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_leak_scan.py +0 -0
  468. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_leak_scan_content.py +0 -0
  469. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_learnings_curate_vendored_promote.py +0 -0
  470. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_learnings_distil_cli.py +0 -0
  471. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_learnings_query.py +0 -0
  472. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_learnings_staging.py +0 -0
  473. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_learnings_suggest_signature_filter.py +0 -0
  474. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_learnings_weights.py +0 -0
  475. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  476. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lifecycle_integration.py +0 -0
  477. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lifetime_accounting.py +0 -0
  478. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_ac_observable.py +0 -0
  479. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_bare_produces_path.py +0 -0
  480. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_boundary_consistency.py +0 -0
  481. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_boundary_extraction.py +0 -0
  482. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_close_intermediate.py +0 -0
  483. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  484. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_closing.py +0 -0
  485. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_closing_criteria.py +0 -0
  486. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_closing_criteria_pristine.py +0 -0
  487. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  488. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_covers_load_wu_invariants.py +0 -0
  489. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_feature_oracle_declared.py +0 -0
  490. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_gate_proportionality.py +0 -0
  491. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_monitoring.py +0 -0
  492. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_oracle_env.py +0 -0
  493. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_plan_contract_fields.py +0 -0
  494. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_plan_next_draft.py +0 -0
  495. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_plan_relative_import.py +0 -0
  496. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_plan_verdict_exempt.py +0 -0
  497. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_planning_sections.py +0 -0
  498. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_produces_driver_helper.py +0 -0
  499. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_produces_satisfiability.py +0 -0
  500. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_roadmap.py +0 -0
  501. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  502. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_lint_verification_reference.py +0 -0
  503. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_live_tests_opt_in.py +0 -0
  504. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_load_bearing_close_lint.py +0 -0
  505. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_attempt_evidence.py +0 -0
  506. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_auto_archive.py +0 -0
  507. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_caveman_preamble.py +0 -0
  508. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_close_intermediate.py +0 -0
  509. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_criteria_skeleton.py +0 -0
  510. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_criteria_survival.py +0 -0
  511. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_defaults_by_type.py +0 -0
  512. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_effort.py +0 -0
  513. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_failure_note_cap.py +0 -0
  514. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_files_changed_guard.py +0 -0
  515. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_gate_budget.py +0 -0
  516. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_gate_needs.py +0 -0
  517. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_model_alias.py +0 -0
  518. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_orchestration.py +0 -0
  519. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_post_pass_invariant.py +0 -0
  520. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_reset_preserving_events.py +0 -0
  521. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_smoke_runner.py +0 -0
  522. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_unsandboxed.py +0 -0
  523. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_worklist_injection.py +0 -0
  524. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_loop_zero_token_guard.py +0 -0
  525. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  526. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_marker_dual_shape.py +0 -0
  527. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_maven_failure_excerpt.py +0 -0
  528. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_max_attempts_configurable.py +0 -0
  529. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_max_open_prs.py +0 -0
  530. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_migrate_legacy.py +0 -0
  531. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_miniyaml_negative.py +0 -0
  532. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_check_isolation.py +0 -0
  533. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_cli.py +0 -0
  534. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_diagnose_auto_warning.py +0 -0
  535. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_durations.py +0 -0
  536. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_finding_context_end_to_end.py +0 -0
  537. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_finding_lifecycle_end_to_end.py +0 -0
  538. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_finding_meta.py +0 -0
  539. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_issue_lifecycle.py +0 -0
  540. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_one_issue_per_fingerprint.py +0 -0
  541. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_quiet_annotation.py +0 -0
  542. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitor_runner_surfaces.py +0 -0
  543. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  544. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitoring_cron_dialect.py +0 -0
  545. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitoring_example.py +0 -0
  546. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitoring_fenced_blocks.py +0 -0
  547. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_monitoring_seed.py +0 -0
  548. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_narrow_fallback_warning.py +0 -0
  549. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_narrow_non_py_produces.py +0 -0
  550. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_needs_decision_outcome.py +0 -0
  551. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_no_pytest_subprocess.py +0 -0
  552. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_notify.py +0 -0
  553. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_notify_escalation.py +0 -0
  554. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_notify_sla.py +0 -0
  555. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_operator_escalation_rule.py +0 -0
  556. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_oracle_set_declared.py +0 -0
  557. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_oracle_unrunnable_attribution.py +0 -0
  558. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_per_run_item_cap_disclosure.py +0 -0
  559. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pick_feature_skill.py +0 -0
  560. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pin_honesty_and_integrity.py +0 -0
  561. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pin_marker_not_inherited.py +0 -0
  562. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pin_marker_spawn_sites.py +0 -0
  563. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_plan_baseline.py +0 -0
  564. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_planned_cost_lint.py +0 -0
  565. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_planning_cost_floor.py +0 -0
  566. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_plugin_prose_cross_refs.py +0 -0
  567. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_plugin_qualified_dispatch.py +0 -0
  568. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_policy_budgets_applied.py +0 -0
  569. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_policy_proposals.py +0 -0
  570. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_policy_review.py +0 -0
  571. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pr_checks_json_field.py +0 -0
  572. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pr_reference_in_title.py +0 -0
  573. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  574. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_prep_halt_operator_surface.py +0 -0
  575. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_prepare_feature.py +0 -0
  576. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_prepare_scaffold_sync.py +0 -0
  577. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_prerun_capture.py +0 -0
  578. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_prerun_oracles.py +0 -0
  579. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_prerun_wiring.py +0 -0
  580. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produced_document_verdict_guard.py +0 -0
  581. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_amendment_e2e.py +0 -0
  582. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_bracket_literal.py +0 -0
  583. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_bracket_wildcard.py +0 -0
  584. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_field.py +0 -0
  585. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_in_diff.py +0 -0
  586. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_justification.py +0 -0
  587. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_recursive_glob.py +0 -0
  588. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_repair_note.py +0 -0
  589. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_semantics_unified.py +0 -0
  590. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_produces_shape.py +0 -0
  591. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_progress_forward_note.py +0 -0
  592. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_progress_lines_end_to_end.py +0 -0
  593. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_promoted_issue_not_redispatched.py +0 -0
  594. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_promotion_ledger.py +0 -0
  595. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_provider_escalation_traces.py +0 -0
  596. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_provision_labels.py +0 -0
  597. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_queue_stalled_adapter.py +0 -0
  598. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rearm_clears_stale_criteria_state.py +0 -0
  599. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rearm_close_broad_run.py +0 -0
  600. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rearm_fold_marker.py +0 -0
  601. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rearm_migration.py +0 -0
  602. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rearm_retained_new_files.py +0 -0
  603. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_reclose_carries_narrow_greens_e2e.py +0 -0
  604. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_red_baseline_never_reused.py +0 -0
  605. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_red_on_base_fetch_and_reason.py +0 -0
  606. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_red_on_base_guardrail.py +0 -0
  607. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_red_on_base_overlay.py +0 -0
  608. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_red_on_base_probe.py +0 -0
  609. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_red_on_base_real_runner.py +0 -0
  610. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_redaction_windows_home.py +0 -0
  611. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rediagnose_on_reopen.py +0 -0
  612. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_reescalation_posts_comment.py +0 -0
  613. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_reflection_required_signals.py +0 -0
  614. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replan_end_to_end.py +0 -0
  615. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replan_event_emission.py +0 -0
  616. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replan_note_collision.py +0 -0
  617. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replan_trigger.py +0 -0
  618. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replay_spin.py +0 -0
  619. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_replay_spin_failing_sets.py +0 -0
  620. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_required_checks.py +0 -0
  621. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_resolve_base.py +0 -0
  622. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_restart_cap_counts_progress.py +0 -0
  623. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_result_block.py +0 -0
  624. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_result_block_audience.py +0 -0
  625. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_result_block_near_miss.py +0 -0
  626. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_result_block_scalar.py +0 -0
  627. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_resume_command_build.py +0 -0
  628. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_retain_on_guard_refusal_docs.py +0 -0
  629. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_archive_cli.py +0 -0
  630. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_archive_shim.py +0 -0
  631. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_archive_skill.py +0 -0
  632. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  633. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_link_gate.py +0 -0
  634. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_roadmap_row_parser.py +0 -0
  635. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_ruff_verdict_lines.py +0 -0
  636. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_rule_import_convergence.py +0 -0
  637. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_run_level_failure_breaker.py +0 -0
  638. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_data_in_sync.py +0 -0
  639. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_doc_hygiene.py +0 -0
  640. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_docs.py +0 -0
  641. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_init.py +0 -0
  642. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_label_provisioning.py +0 -0
  643. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_manifest.py +0 -0
  644. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_resources.py +0 -0
  645. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_seed_sanity.py +0 -0
  646. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_skew_halts.py +0 -0
  647. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_upgrade.py +0 -0
  648. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_scaffold_wiring.py +0 -0
  649. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_schedule_dialect.py +0 -0
  650. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_seed_documents_prerun_keys.py +0 -0
  651. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_service_bus_dlq_adapter.py +0 -0
  652. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_session_env_preflight.py +0 -0
  653. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_aliases.py +0 -0
  654. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_backfill_apply.py +0 -0
  655. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_backfill_bugs_only.py +0 -0
  656. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_backfill_end_to_end.py +0 -0
  657. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_backfill_limit.py +0 -0
  658. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_backfill_marker.py +0 -0
  659. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_backfill_run.py +0 -0
  660. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_label_projection.py +0 -0
  661. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_reconciliation.py +0 -0
  662. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_severity_rubric.py +0 -0
  663. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_shipped_templates_parse.py +0 -0
  664. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_skill_discovery_links.py +0 -0
  665. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_skills_vendored_in_sync.py +0 -0
  666. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_spec_work_unit_rules.py +0 -0
  667. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_spend_counts_cache_tokens.py +0 -0
  668. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_spinning_rearm_gate.py +0 -0
  669. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_spinning_repeat_same_failing_set.py +0 -0
  670. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_spinout_brief_end_to_end.py +0 -0
  671. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_spinout_brief_replan_option.py +0 -0
  672. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_squash_commit_hook_crash.py +0 -0
  673. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_stop_rationale_captured.py +0 -0
  674. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_stranded_halt_cause.py +0 -0
  675. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_surefire_run_level_signature.py +0 -0
  676. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_surefire_verdict_lines.py +0 -0
  677. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_template_closing_shapes.py +0 -0
  678. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_terminal_auto_close_changelog.py +0 -0
  679. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_terminal_flip_ownership.py +0 -0
  680. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_terminal_flips.py +0 -0
  681. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_terminal_gate_message.py +0 -0
  682. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_tiered_verification_e2e.py +0 -0
  683. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_timeout_kill_portable.py +0 -0
  684. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage.py +0 -0
  685. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_apply.py +0 -0
  686. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_projects_severity_label.py +0 -0
  687. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_reads_comments.py +0 -0
  688. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_severity_classify.py +0 -0
  689. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_severity_end_to_end.py +0 -0
  690. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_severity_label_conflict.py +0 -0
  691. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_severity_write.py +0 -0
  692. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_skill_contract.py +0 -0
  693. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_triage_skips_agent_escalations.py +0 -0
  694. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_tsc_verdict_lines.py +0 -0
  695. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_umbrella_version_resolution.py +0 -0
  696. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_unreadable_pr_listing_holds_pr_work.py +0 -0
  697. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_untracked_feature_folder.py +0 -0
  698. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_untracked_file_absorption.py +0 -0
  699. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_upgrade_gitignore_lines.py +0 -0
  700. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_upgrade_integration.py +0 -0
  701. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_upgrade_merge_gate.py +0 -0
  702. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_verdict_coupling.py +0 -0
  703. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_verify_empty_gate_set.py +0 -0
  704. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_version_consistency.py +0 -0
  705. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_version_skew.py +0 -0
  706. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_windows_gate_exec.py +0 -0
  707. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_worktree_restore.py +0 -0
  708. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/tests/test_wu_execution_metadata.py +0 -0
  709. {specfuse_loop-0.30.0 → specfuse_loop-0.31.1}/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.30.0
3
+ Version: 0.31.1
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
@@ -152,10 +152,10 @@ specfuse run # the real run
152
152
 
153
153
  This repository is also a **self-demonstrating reference installation**: its own
154
154
  `.specfuse/` contains a worked example feature
155
- (`features/FEAT-2026-0001-health-endpoint/`). From the repo root you can run:
155
+ (`features/FEAT-LOOP-2026-0001-health-endpoint/`). From the repo root you can run:
156
156
 
157
157
  ```bash
158
- python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-2026-0001-health-endpoint
158
+ python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-LOOP-2026-0001-health-endpoint
159
159
  python .specfuse/scripts/loop.py --dry-run
160
160
  ```
161
161
 
@@ -180,7 +180,7 @@ specfuse-loop/
180
180
  ├── skills/verification/SKILL.md
181
181
  ├── scripts/{loop.py, lint_plan.py, gh_features.py, adopt_feature.py, gh_backend.py}
182
182
  ├── templates/{PLAN,GATE,WU}.template.md
183
- └── features/FEAT-2026-0001-health-endpoint/ (the worked example)
183
+ └── features/FEAT-LOOP-2026-0001-health-endpoint/ (the worked example)
184
184
  ```
185
185
 
186
186
  `specfuse init` also ships the durable docs — `methodology.md`, `skills.md`, and
@@ -119,10 +119,10 @@ specfuse run # the real run
119
119
 
120
120
  This repository is also a **self-demonstrating reference installation**: its own
121
121
  `.specfuse/` contains a worked example feature
122
- (`features/FEAT-2026-0001-health-endpoint/`). From the repo root you can run:
122
+ (`features/FEAT-LOOP-2026-0001-health-endpoint/`). From the repo root you can run:
123
123
 
124
124
  ```bash
125
- python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-2026-0001-health-endpoint
125
+ python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-LOOP-2026-0001-health-endpoint
126
126
  python .specfuse/scripts/loop.py --dry-run
127
127
  ```
128
128
 
@@ -147,7 +147,7 @@ specfuse-loop/
147
147
  ├── skills/verification/SKILL.md
148
148
  ├── scripts/{loop.py, lint_plan.py, gh_features.py, adopt_feature.py, gh_backend.py}
149
149
  ├── templates/{PLAN,GATE,WU}.template.md
150
- └── features/FEAT-2026-0001-health-endpoint/ (the worked example)
150
+ └── features/FEAT-LOOP-2026-0001-health-endpoint/ (the worked example)
151
151
  ```
152
152
 
153
153
  `specfuse init` also ships the durable docs — `methodology.md`, `skills.md`, and
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specfuse-loop"
7
- version = "0.30.0"
7
+ version = "0.31.1"
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"
@@ -2,7 +2,7 @@
2
2
  # Licensed under the Apache License, Version 2.0. See LICENSE.
3
3
  """The specfuse-agent runner (FEAT-2026-0049): a thin conductor over the
4
4
  existing loop driver and skills. See `.specfuse/features/
5
- FEAT-2026-0049-specfuse-agent-runner/PLAN.md` for why this is a sibling
5
+ FEAT-LOOP-2026-0049-specfuse-agent-runner/PLAN.md` for why this is a sibling
6
6
  package to `specfuse/loop/` and `specfuse/monitor/` rather than living in
7
7
  either.
8
8
  """
@@ -33,6 +33,7 @@ import re
33
33
  from pathlib import Path
34
34
 
35
35
  from . import _miniyaml
36
+ from . import ids as _ids
36
37
  from .lint_roadmap import roadmap_statuses
37
38
 
38
39
  __all__ = (
@@ -62,7 +63,7 @@ PROVIDER_VALUES = frozenset({"discord", "slack", "teams", "none"})
62
63
 
63
64
  REQUIRED_TOP_LEVEL_FIELDS = ("version", "queue", "rules", "budgets", "escalation")
64
65
 
65
- _FEAT_ID_RE = re.compile(r"^FEAT-\d{4}-\d{4}$")
66
+ _FEAT_ID_RE = _ids.FEATURE_ID_RE
66
67
 
67
68
  _REQUIRED_BUDGET_FIELDS = ("max_tokens_per_run", "max_open_prs", "max_items_per_day")
68
69
  _REQUIRED_ESCALATION_FIELDS = ("webhook_env", "assignee", "quiet_hours", "sla_hours")
@@ -644,7 +645,7 @@ def resolve_severity_aliases(path: "str | Path | None" = None) -> dict:
644
645
  return resolved
645
646
 
646
647
 
647
- def validate_agent_policy(path: str | Path | None = None) -> list[str]:
648
+ def validate_agent_policy(path: str | Path | None = None, repo_root: str | Path | None = None) -> list[str]:
648
649
  """Validate *path* (default ``.specfuse/agent-policy.yml``); return findings.
649
650
 
650
651
  Empty list means valid. A missing file, a parse failure, and a non-mapping
@@ -684,12 +685,12 @@ def validate_agent_policy(path: str | Path | None = None) -> list[str]:
684
685
  findings.extend(_check_escalation(parsed["escalation"]))
685
686
 
686
687
  if "queue" in parsed and isinstance(parsed["queue"], list):
687
- findings.extend(_check_queue_against_roadmap(parsed["queue"]))
688
+ findings.extend(_check_queue_against_roadmap(parsed["queue"], repo_root))
688
689
 
689
690
  return findings
690
691
 
691
692
 
692
- def _check_queue_against_roadmap(queue: list) -> list[str]:
693
+ def _check_queue_against_roadmap(queue: list, repo_root: "str | Path | None" = None) -> list[str]:
693
694
  """Cross-check queue entries against roadmap.md's FEAT-ID status.
694
695
 
695
696
  ERROR when a queue entry has no roadmap row at all (unresolvable, a
@@ -699,15 +700,18 @@ def _check_queue_against_roadmap(queue: list) -> list[str]:
699
700
  per the roadmap's own status legend. Skipped without error when
700
701
  roadmap.md is absent, so a policy file may exist before a roadmap does.
701
702
  """
702
- statuses = roadmap_statuses()
703
+ statuses = roadmap_statuses(repo_root)
703
704
  if not statuses:
704
705
  return []
706
+ root = Path(repo_root) if repo_root is not None else Path.cwd()
705
707
 
706
708
  findings: list[str] = []
707
709
  for entry in queue:
708
710
  if not isinstance(entry, str) or not _FEAT_ID_RE.match(entry):
709
711
  continue
710
712
  status = statuses.get(entry)
713
+ if status is None:
714
+ status = statuses.get(_ids.resolve(root, entry))
711
715
  if status is None:
712
716
  findings.append(f"ERROR: queue: {entry!r} has no row in roadmap.md")
713
717
  elif status in _DONE_OR_ABANDONED:
@@ -725,7 +729,7 @@ def _check_version(version: object) -> list[str]:
725
729
 
726
730
  def _check_queue(queue: object) -> list[str]:
727
731
  if not isinstance(queue, list):
728
- return ["ERROR: 'queue' must be a list of FEAT-YYYY-NNNN strings"]
732
+ return ["ERROR: 'queue' must be a list of FEAT-YYYY-NNNN or FEAT-<KEY>-YYYY-NNNN strings"]
729
733
 
730
734
  findings: list[str] = []
731
735
  seen: set[str] = set()
@@ -733,7 +737,7 @@ def _check_queue(queue: object) -> list[str]:
733
737
  if not isinstance(entry, str) or not _FEAT_ID_RE.match(entry):
734
738
  findings.append(
735
739
  f"ERROR: queue[{index}]: {entry!r} does not match "
736
- f"FEAT-YYYY-NNNN"
740
+ f"FEAT-YYYY-NNNN or FEAT-<KEY>-YYYY-NNNN"
737
741
  )
738
742
  continue
739
743
  if entry in seen:
@@ -884,7 +888,7 @@ def _check_feature_overrides(overrides: object) -> list[str]:
884
888
  if not _FEAT_ID_RE.match(str(feat_id)):
885
889
  findings.append(
886
890
  f"ERROR: rules.features.overrides: key {feat_id!r} does not "
887
- f"match FEAT-YYYY-NNNN"
891
+ f"match FEAT-YYYY-NNNN or FEAT-<KEY>-YYYY-NNNN"
888
892
  )
889
893
  if value not in GATE_REVIEW_VALUES:
890
894
  findings.append(
@@ -10,8 +10,8 @@ structured per-class verdict. Mirrors the shape of `gate_eval.py`'s
10
10
  `evaluate_auto_close`: pure evaluation over files, a structured decision
11
11
  object, a formatter for the event trail, no side effects. Arming and closing
12
12
  are deliberately separate decisions with different inputs — this module does
13
- not fold into `gate_eval.py`, does not import from it, and does not import
14
- from `loop.py` (the dependency points the other way: `loop.py` will call
13
+ not fold into `gate_eval.py` (it reads only the `NON_SUBSTANTIVE_TYPES`
14
+ constant from it) and does not import from `loop.py` (the dependency points the other way: `loop.py` will call
15
15
  into this module, T04).
16
16
 
17
17
  The organizing principle: model-authored signals may only veto; only
@@ -38,6 +38,7 @@ from pathlib import Path
38
38
  from . import _miniyaml
39
39
  from .closing_requirements import gate_review_filename
40
40
  from .cost import wu_lifetime_cost_usd
41
+ from .gate_eval import NON_SUBSTANTIVE_TYPES
41
42
  from .plan_baseline import load_baseline, load_plan_graph
42
43
 
43
44
  PREDICATE_VERSION = "v1"
@@ -110,6 +111,7 @@ JUDGE_MODULES = tuple(
110
111
  "criteria_state.py",
111
112
  "lint_closing.py",
112
113
  "lint_plan.py",
114
+ "ids.py", # supplies lint_plan's CORRELATION_ID_RE; an edit changes what the close's lint accepts
113
115
  "plan_baseline.py", # the baseline retroactive_edits compares to
114
116
  "roadmap_archive.py", # CLI entry for auto_archive_feature; assert_terminal_flips_fired depends on it
115
117
  # The driver: dispatch guards, halt classification, the produces gate,
@@ -629,6 +631,114 @@ def evaluate_arm_predicate(feature_dir: Path, just_closed_gate: int) -> ArmDecis
629
631
  )
630
632
 
631
633
 
634
+ # Literal acknowledgement entry covering any `produces_driver_helper` hit.
635
+ DRIVER_HELPER_ACK_KEY = "produces_driver_helper"
636
+ # Reason prefix of a `clean` verdict that is clean only because of an acknowledgement.
637
+ ACKNOWLEDGED_REASON_PREFIX = "acknowledged by operator: "
638
+
639
+
640
+ def _terminal_judge_hits(
641
+ feature_dir: Path, gate_number: int
642
+ ) -> "tuple[list[tuple[str, str]], dict | None]":
643
+ """The judge-surface hits of `gate_number`'s done substantive units, each as
644
+ ``(acknowledgement key, rendered hit)``, plus the close unit's
645
+ acknowledgement (or None). Shared by the verdict and the brief's key list so
646
+ the two cannot disagree on what an acknowledgement must name."""
647
+ feature_dir = Path(feature_dir)
648
+ plan = load_plan_graph(feature_dir)
649
+ gate = next(
650
+ (g for g in plan.get("gates", []) or [] if g.get("gate") == gate_number),
651
+ None,
652
+ )
653
+ hits: list[tuple[str, str]] = [] # (acknowledgement key, rendered hit)
654
+ acknowledgement: "dict | None" = None
655
+ for ref in (gate.get("work_units", []) or []) if gate else []:
656
+ wu = _read_wu(feature_dir, ref)
657
+ if wu is None:
658
+ continue
659
+ if wu["type"] == "close" and acknowledgement is None:
660
+ acknowledgement = _read_judge_edit_acknowledgement(feature_dir / ref["file"])
661
+ if wu["type"] in NON_SUBSTANTIVE_TYPES or wu["status"] != "done":
662
+ continue
663
+ for p in wu["produces"]:
664
+ if _matches_judge_path(p):
665
+ hits.append((p, f"{wu['id']} produces {p}"))
666
+ if wu["produces_driver_helper"]:
667
+ hits.append((
668
+ DRIVER_HELPER_ACK_KEY,
669
+ f"{wu['id']} declares produces_driver_helper "
670
+ f"{wu['produces_driver_helper']!r}",
671
+ ))
672
+ return hits, acknowledgement
673
+
674
+
675
+ def terminal_judge_edit_ack_keys(feature_dir: Path, gate_number: int) -> "list[str]":
676
+ """The sorted entries a `judge_edit_acknowledged.paths` list must contain to
677
+ cover every current hit of `gate_number`: each judge-surface path, plus
678
+ `DRIVER_HELPER_ACK_KEY` when any unit declares `produces_driver_helper`.
679
+ Pure; empty when nothing hits."""
680
+ hits, _ = _terminal_judge_hits(Path(feature_dir), gate_number)
681
+ return sorted({key for key, _ in hits})
682
+
683
+
684
+ def evaluate_terminal_judge_editing(feature_dir: Path, gate_number: int) -> ClassVerdict:
685
+ """Report which done substantive units of `gate_number` touch a judge surface.
686
+
687
+ Not a successor-arm class: it is absent from `CLASS_NAMES` and `would_arm`
688
+ never reads it. It is the terminal gate's counterpart of class 2 -- the arm
689
+ predicate looks at *drafted* units of the next gate; nothing arms after the
690
+ terminal gate, so this looks at the units that already ran and are about to
691
+ be closed over. Pure: reads PLAN.md and the gate's unit files only.
692
+ """
693
+ hits, acknowledgement = _terminal_judge_hits(feature_dir, gate_number)
694
+ if not hits:
695
+ return ClassVerdict(
696
+ "clean", "no done substantive unit touches judge-path surfaces"
697
+ )
698
+ covered = set(acknowledgement["paths"]) if acknowledgement else set()
699
+ uncovered = [text for key, text in hits if key not in covered]
700
+ if uncovered:
701
+ return ClassVerdict("fired", "; ".join(uncovered))
702
+ names = sorted({key for key, _ in hits})
703
+ return ClassVerdict("clean", f"{ACKNOWLEDGED_REASON_PREFIX}{', '.join(names)}")
704
+
705
+
706
+ def _read_judge_edit_acknowledgement(close_file: Path) -> "dict | None":
707
+ """The operator-authored `judge_edit_acknowledged` field of a close unit, or
708
+ None when absent or malformed (a malformed field covers nothing; lint_plan
709
+ reports it). The driver never writes this field."""
710
+ fm, _ = _parse_frontmatter(close_file.read_text())
711
+ raw = fm.get("judge_edit_acknowledged")
712
+ if not isinstance(raw, dict):
713
+ return None
714
+ paths, reason = raw.get("paths"), raw.get("reason")
715
+ if (not isinstance(paths, list) or not paths
716
+ or not all(isinstance(p, str) for p in paths)
717
+ or not isinstance(reason, str) or not reason.strip()):
718
+ return None
719
+ return {"paths": paths, "reason": reason}
720
+
721
+
722
+ def terminal_judge_edit_acknowledgement(
723
+ feature_dir: Path, gate_number: int
724
+ ) -> "dict | None":
725
+ """The acknowledgement on `gate_number`'s close unit (``{"paths", "reason"}``)
726
+ or None."""
727
+ feature_dir = Path(feature_dir)
728
+ plan = load_plan_graph(feature_dir)
729
+ gate = next(
730
+ (g for g in plan.get("gates", []) or [] if g.get("gate") == gate_number),
731
+ None,
732
+ )
733
+ for ref in (gate.get("work_units", []) or []) if gate else []:
734
+ wu = _read_wu(feature_dir, ref)
735
+ if wu is not None and wu["type"] == "close":
736
+ ack = _read_judge_edit_acknowledgement(feature_dir / ref["file"])
737
+ if ack is not None:
738
+ return ack
739
+ return None
740
+
741
+
632
742
  def _format_decision(decision: ArmDecision) -> str:
633
743
  """Render an ArmDecision to the canonical event-trail block shape."""
634
744
  lines: list[str] = [
@@ -29,6 +29,8 @@ from __future__ import annotations
29
29
  import re
30
30
  from dataclasses import dataclass, field
31
31
 
32
+ from specfuse.loop import ids
33
+
32
34
  ENTRY_CLASSES = ("added", "changed", "fixed", "breaking")
33
35
 
34
36
  _UNRELEASED_HEADING_RE = re.compile(r"^##\s+\[Unreleased\]\s*$")
@@ -36,7 +38,9 @@ _RELEASED_HEADING_RE = re.compile(r"^##\s+\[(?P<version>[^\]]+)\]\s*-\s*(?P<date
36
38
  _ANY_H2_RE = re.compile(r"^##\s+")
37
39
  _CLASS_HEADING_RE = re.compile(r"^###\s+(?P<name>\S.*?)\s*$")
38
40
  _ENTRY_RE = re.compile(r"^-\s+(?P<summary>.+?)\s+\((?P<trace>[^()]+)\)\s*$")
39
- _TRACE_RE = re.compile(r"^(FEAT-\d{4}-\d{4}(?:/[A-Za-z0-9-]+)?|#\d+)$")
41
+ _TRACE_RE = re.compile(
42
+ r"^(" + ids.FEATURE_ID_PATTERN + r"(?:/[A-Za-z0-9-]+)?|#\d+)$"
43
+ )
40
44
 
41
45
 
42
46
  @dataclass
@@ -0,0 +1 @@
1
+ 0.31.1
@@ -48,12 +48,19 @@ cleared by editing driver code; it clears by changing the plan's numbers.
48
48
  file under a judge-path surface — paths that would let a work unit edit its
49
49
  own verification.
50
50
 
51
- **Does not cover the terminal gate.** The class reads only the successor
52
- gate's drafted units. A terminal gate has no successor, so what its own units
53
- change is never checked; a single-gate feature is never checked at all. The
54
- terminal close goes straight to the judge, and no judge-path check sits on
55
- that path (#3542). A feature that edits a judge surface in its last gate should
56
- run under `review`.
51
+ **Terminal gate.** The class reads only the successor gate's drafted units,
52
+ and a terminal gate has no successor. A separate terminal judge-editing check
53
+ covers it: it halts before the close when a done substantive unit of the last
54
+ gate declares a judge-surface path, at every autonomy level. It is not one of
55
+ the eight arm classes and does not touch `would_arm`. The operator clears the
56
+ stop by writing `judge_edit_acknowledged` (`paths` and `reason`) on the close
57
+ unit and running `/unblock-wu`. It uses the same `JUDGE_PATHS`, so the
58
+ universal entries (`.specfuse/verification.yml`, `.specfuse/hooks/`,
59
+ `.specfuse/rules/`, `.github/workflows/`, `pyproject.toml`) also reach consumer
60
+ repositories: a feature there whose last gate edits its CI workflow or
61
+ `pyproject.toml` halts once. The check reads declared `produces:` paths only,
62
+ so an edit a unit never declares is not seen, and `pyproject.toml` matches
63
+ whole-file, so a dependency bump fires it.
57
64
 
58
65
  **Fires when:** a drafted WU's `produces:` list contains a path matching one
59
66
  of `JUDGE_PATHS` — `.specfuse/verification.yml`, `.specfuse/hooks/`,
@@ -112,7 +112,7 @@ Two ways to create a feature folder under `.specfuse/features/`:
112
112
  - **By hand:** start from the bare templates in `.specfuse/templates/`
113
113
  (`PLAN`, `GATE`, `WU`) and fill in a small first feature. (The
114
114
  `specfuse/loop` source repo also carries a worked example,
115
- `FEAT-2026-0001-health-endpoint`, if you want a complete reference to copy
115
+ `FEAT-LOOP-2026-0001-health-endpoint`, if you want a complete reference to copy
116
116
  from.)
117
117
 
118
118
  A feature folder holds:
@@ -141,7 +141,13 @@ specfuse lint .specfuse/features/FEAT-YYYY-NNNN-your-feature
141
141
  ```
142
142
 
143
143
  The linter checks structure: every WU has the five mandatory sections, the closing
144
- sequence is present and well-formed, dependencies resolve, IDs are well-formed.
144
+ sequence is present and well-formed, dependencies resolve, IDs are well-formed
145
+ (both `FEAT-YYYY-NNNN` and the keyed `FEAT-<KEY>-YYYY-NNNN` form).
146
+
147
+ Running several repos? Optionally set `ids.key` in `.specfuse/layout.yml` so new
148
+ IDs read `FEAT-<KEY>-YYYY-NNNN`. Pick a short key distinct across the repos of
149
+ one project (uniqueness is by convention). Existing IDs are never renamed;
150
+ `ids migrate --planned-only` re-keys folderless planned rows only.
145
151
  Fix anything it flags before dispatching — it's far cheaper than a failed
146
152
  dispatch.
147
153
 
@@ -13,7 +13,8 @@ reference.
13
13
  intends to build, one row each. Add to it with `/roadmap-add`; pick what to
14
14
  build next with `/pick-feature`.
15
15
 
16
- **Feature** — one substantial piece of work, `FEAT-YYYY-NNNN`, with a folder
16
+ **Feature** — one substantial piece of work, `FEAT-YYYY-NNNN` (`FEAT-<KEY>-YYYY-NNNN` when
17
+ `ids.key` is set in `.specfuse/layout.yml`; legacy IDs are never renamed), with a folder
17
18
  under `.specfuse/features/`. Its `PLAN.md` holds the goal, the gates, and the
18
19
  budget. Roughly: a thing you would describe in a sentence to someone who does
19
20
  not work on the project.
@@ -24,7 +25,7 @@ and you decide** — everything inside one runs unattended; the boundary between
24
25
  two is yours.
25
26
 
26
27
  **Work unit (WU)** — the smallest dispatchable piece: one file, one job, one
27
- fresh session. `FEAT-YYYY-NNNN/T01`. Everything the session needs is in that
28
+ fresh session. `FEAT-YYYY-NNNN/T01` (keyed: `FEAT-<KEY>-YYYY-NNNN/T01`). Everything the session needs is in that
28
29
  file, because it starts with no memory of anything before it.
29
30
 
30
31
  **Verification** — the commands in `.specfuse/verification.yml` that decide
@@ -34,7 +34,8 @@ on both surfaces.
34
34
  decision or dependency. The absence of a named blocker is the entire
35
35
  difference from `blocked`.
36
36
  - **Feature** — a spec-driven *or directly-authored* unit of value, identified by
37
- a correlation ID `FEAT-YYYY-NNNN`. A feature owns an ordered list of gates.
37
+ a correlation ID `FEAT-YYYY-NNNN` (or `FEAT-<KEY>-YYYY-NNNN` when the repo sets
38
+ `ids.key`; see *Project key* below). A feature owns an ordered list of gates.
38
39
  - **Gate** — a milestone partition of a feature: an ordered batch of substantive
39
40
  work units followed by a mandatory closing sequence and a human review-and-arm
40
41
  checkpoint. Gates are numbered within a feature.
@@ -49,6 +50,30 @@ on both surfaces.
49
50
  It carries its own prompt and is the contract between the planner and the
50
51
  executor.
51
52
 
53
+ **Project key.** A repo may set an optional `ids.key` in `.specfuse/layout.yml`.
54
+ With a key set, new features are allocated as `FEAT-<KEY>-YYYY-NNNN`, and the
55
+ folder anchor uses the lower-cased key (`FEAT-<key>-YYYY-NNNN-slug`). Numbering
56
+ stays per repo and continues from the highest ordinal of either form. Legacy
57
+ unkeyed IDs are permanent: existing IDs are never renamed. The one exception to
58
+ leaving old rows alone is `ids migrate --planned-only`, which re-keys folderless
59
+ planned roadmap rows. Pick a short key distinct across the repos of one project;
60
+ uniqueness is by convention, not enforced. No prompt asks for the key; it is read
61
+ from `layout.yml`. The ID shape lives in `specfuse.loop.ids`.
62
+
63
+ **Renaming existing features.** `ids migrate --all` is the opt-in full rename: it
64
+ re-keys every feature of the repository, rewriting folder names, PLAN and WU
65
+ frontmatter, roadmap rows and anchors, and `events.jsonl` correlation IDs, and
66
+ records each old-to-new pair in `.specfuse/id-aliases.yml`. It cannot reach git
67
+ history, GitHub issue and PR titles, or other repositories, which keep the old
68
+ IDs. The loop resolves an old ID through the alias map (one hop) wherever an
69
+ operator types one: `--feature`, `gate_eval`, and `[FEAT-…]` issue titles. Prose
70
+ mentions are listed for review (`--include-prose` rewrites them) rather than
71
+ rewritten silently.
72
+ `--rehearse` proves a rename first: it applies it (with post-apply validation) on a
73
+ throwaway worktree of `HEAD`, runs the `tests` gate command there (or
74
+ `--rehearse-command`), reports the failing tests, and removes the worktree; the real
75
+ tree is never written and must have no uncommitted tracked changes.
76
+
52
77
  The correlation ID threads the entire lifecycle — it appears in the feature
53
78
  folder name, the WU file, every event-log entry, the branch, the commit trailer
54
79
  (`Feature: FEAT-YYYY-NNNN/TNN`), and (in the orchestrator) the GitHub issue.
@@ -406,7 +431,7 @@ Every dispatched attempt emits exactly one `attempt_outcome` event to
406
431
  `outcome`, `failure_class`, `failure_signature`, `failure_excerpt`,
407
432
  `failing_tests`, `cost_usd`, `duration_seconds`, `attempt`, and
408
433
  `re_arm_count`. For the field-by-field schema see
409
- `.specfuse/features/FEAT-2026-0016-attempt-outcome-rearm-contract/PLAN.md`
434
+ `.specfuse/features/FEAT-LOOP-2026-0016-attempt-outcome-rearm-contract/PLAN.md`
410
435
  § "Event payload shape — `attempt_outcome` v1". The full payload is
411
436
  not restated here (one fact, one home).
412
437
 
@@ -796,11 +821,13 @@ single flip site. What changed is what an armed gate is armed *on*. Before the
796
821
  judge, an `auto` feature let the session that did the work write the verdict
797
822
  that advanced the feature; now a fresh evaluator that never sees that session's
798
823
  prose writes it, and can only lower it. The `judge_editing` stop class vetoes
799
- arming a successor gate whose drafted units would edit a judge surface. It
800
- does **not** cover the terminal gate's own units, so a single-gate feature,
801
- or the last gate of any feature, that edits the loop's verdict machinery
802
- reaches its close with no human checkpoint under `auto` (#3542). Choose
803
- `review` for such a feature.
824
+ arming a successor gate whose drafted units would edit a judge surface. The
825
+ terminal gate has no successor, so a separate check covers it: when a done
826
+ substantive unit of the last gate declares a judge-surface path, the driver
827
+ halts before the close, at every autonomy level, and the operator clears the
828
+ stop by writing `judge_edit_acknowledged` on the close unit and running
829
+ `/unblock-wu`. `review` adds no terminal checkpoint; choose it only when a
830
+ wrong arm is expensive to undo.
804
831
 
805
832
  Tighten to `review` when the judge cannot do its job — a feature whose criteria
806
833
  are not judgeable from the evidence bundle (see `judge_disabled` in §3), or one
@@ -46,7 +46,10 @@ flips `blocked` → `active` (or `planned`) and removes the `**Blocked by.**` bl
46
46
 
47
47
  ## Notes
48
48
 
49
- - Correlation IDs are allocated here, sequentially per year: `FEAT-YYYY-NNNN`. Work
49
+ - Correlation IDs are allocated here, sequentially per year per repo: `FEAT-YYYY-NNNN`,
50
+ or `FEAT-<KEY>-YYYY-NNNN` when `ids.key` is set in `.specfuse/layout.yml` (numbering
51
+ continues from the highest ordinal of either form; legacy IDs are never renamed;
52
+ `ids migrate --planned-only` re-keys folderless planned rows). Work
50
53
  units take `FEAT-YYYY-NNNN/TNN`. The same scheme threads commits (trailer
51
54
  `Feature: FEAT-YYYY-NNNN/TNN`), the per-feature event log, and — at fold-in —
52
55
  GitHub issues across repos.
@@ -24,6 +24,16 @@ Two namespaces, four canonical shapes:
24
24
  - `YYYY` is the four-digit calendar year in which the feature was created.
25
25
  - `NNNN` is a zero-padded four-digit ordinal, unique within that year's roadmap.
26
26
  - Example: `FEAT-2026-0042`.
27
+ - **Keyed feature-level (optional):** `FEAT-<KEY>-YYYY-NNNN`
28
+ - `KEY` is a per-repository project key: an uppercase letter followed by one to
29
+ five uppercase letters or digits (`[A-Z][A-Z0-9]{1,5}`). A repository opts in by
30
+ setting it once (in the loop, `ids.key` in `.specfuse/layout.yml`); without a key,
31
+ IDs keep the unkeyed shape above.
32
+ - The key makes an ID say which repository it belongs to, so the same ordinal in
33
+ two repositories of one project no longer collides. Numbering stays per
34
+ repository: the ordinal continues from the highest `NNNN` of either shape.
35
+ - Unkeyed IDs that already exist stay valid permanently and are never renamed.
36
+ - Example: `FEAT-ACME-2026-0042`; anchor `feat-acme-2026-0042`.
27
37
  - **Task-level:** `FEAT-YYYY-NNNN/<task-id>`
28
38
  - The feature-level ID, a literal `/`, then the task identifier.
29
39
  - Substantive units use `TNN` — `T` followed by a zero-padded two-digit ordinal,
@@ -66,7 +76,7 @@ Collisions between the two namespaces are structurally impossible.
66
76
  The combined pattern accepted across all shapes is:
67
77
 
68
78
  ```
69
- ^(FEAT-\d{4}-\d{4}(/(T\d{2}(H\d*)?|G\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?|INIT-\d{4}-\d{4}/F\d{2}(/(T\d{2}(H\d*)?|G\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?)$
79
+ ^(FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?\d{4}-\d{4}(/(T\d{2}(H\d*)?|G\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?|INIT-\d{4}-\d{4}/F\d{2}(/(T\d{2}(H\d*)?|G\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?)$
70
80
  ```
71
81
 
72
82
  A string that does not match is malformed.
@@ -123,7 +133,9 @@ that materially describes the work, err toward including the ID.
123
133
  ## Generating the next ID
124
134
 
125
135
  **Feature-level.** The next ordinal for year `YYYY` is one greater than the largest
126
- `NNNN` that currently appears in `.specfuse/features/FEAT-YYYY-*/`. If no feature
136
+ `NNNN` that currently appears in `.specfuse/features/FEAT-YYYY-*/` or, in a keyed
137
+ repository, `.specfuse/features/FEAT-<KEY>-YYYY-*/` (both shapes count). A keyed
138
+ repository emits the keyed shape. If no feature
127
139
  for `YYYY` exists yet, start at `0001`. Padding is always four digits: `0001`,
128
140
  `0042`, `1234`. Year rollover does not continue the previous year's counter — each
129
141
  year starts fresh at `0001`.
@@ -3,7 +3,7 @@
3
3
  "$id": "https://specfuse.dev/loop/schemas/driver-event.schema.json",
4
4
  "title": "Driver-local envelope registry",
5
5
  "description": "Additive to the vendored orchestrator envelope (event.schema.json): shapes the loop driver emits with no orchestrator-side counterpart. validate_event.py resolves each shape against the vendored envelope first; only when absent there does it fall through to this registry. `event_types` (FEAT-2026-0060) widens the event_type enum; task_started, task_completed, and human_escalation are already sanctioned in the vendored enum and must not be duplicated here. `correlation_id` (FEAT-2026-0073) widens the correlation_id pattern to admit the closing-sequence and hygiene shapes `.specfuse/rules/correlation-ids.md` documents; `closing_names` and `hygiene_suffix_pattern` here must state the same set of shapes as that rules file's combined pattern (and as specfuse/loop/lint_plan.py's CORRELATION_ID_RE, which independently enforces the same contract on PLAN.md graphs).",
6
- "$comment": "FEAT-2026-0060/T01: event_types derived from the union of every build_event(...) call site in specfuse/loop/loop.py and a corpus sweep of every .specfuse/features/*/events.jsonl, subtracting the vendored envelope enum. attempt_outcome and gate_reached were already-known precedent (loop.py's build_arm_predicate_event docstring); auto_close_decision, arm_predicate_evaluated, re_arm_dispatched, plan_next_draft_lint, and unsandboxed_dispatch are additional driver-local mechanics the orchestrator has no concept of. gate_auto_armed (loop.py build_event call under gate_events.append) and re_arm_rejected (loop.py build_event call under the spinning_reproduction_missing re-arm check) are emitted by real code paths that have never fired in a recorded run, so a corpus-only sweep cannot see them; they are included because call-site derivation does. FEAT-2026-0073/T01: correlation_id added — closing_names and hygiene_suffix_pattern mirror correlation-ids.md's documented shapes and lint_plan.py's CORRELATION_ID_RE verbatim, so the envelope, the rules file, and the PLAN-graph linter state one contract. FEAT-2026-0075/T03: driver_staleness_detected added — emitted at gate completion when a work unit's squash diff touched the driver's own importable surface, naming the editing unit, the driver paths it touched, and the IDs of units this process dispatched afterward (which ran the pre-edit module). FEAT-2026-0100/T02: judged added — emitted once per terminal close after the closing-deliverable guards, carrying the close's own verdict, the judge session's verdict (null when the judge was disabled, timed out, or returned nothing parseable, with the reason beside it), and whether the judge lowered it. FEAT-2026-0109/T01: baseline_attribution added — emitted when the retroactive per-attempt-failure baseline probe (attribute_failure_to_baseline) runs fresh, carrying the gate, the failing-gate set, and the unit whose failed attempt triggered the probe. FEAT-2026-0109/T06: broad_run_result added — emitted whenever the once-per-gate broad verification pass (gate_broad_run_check) actually runs (never on a tree-unchanged dedup skip), carrying the gate, the tree it was measured on, and its pass/fail outcome; a failing broad run additionally raises the existing human_escalation event type with reason: broad_run_gate_failure, which needs no schema addition since human_escalation is already sanctioned in the vendored envelope enum. FEAT-2026-0109/T08: driver_build_pinned added — emitted once per process, before any work unit is dispatched, when this process is itself a materialized pin (`_reexec_pinned`); carries the pin's tree hash and its path so an operator or test can read what build actually ran without inferring it from `ps` output. `driver_staleness_detected` needed no addition here: this unit reuses it with `halted: false` for the pinned, non-halting case (the registry constrains the type list, not the payload), adding `pinned_tree` / `next_pin_tree` fields alongside the existing `halted: true` shape. FEAT-2026-0104/T04: replan added — emitted once a re-plan turn (T03's run_replan_turn) rewrites a unit's body ahead of its last permitted attempt, so gate_eval.py's evaluate_auto_close (check 2, already reading this event_type since FEAT-2026-0018/T02) can disable auto-close for a gate that needed one. FEAT-2026-0115/T01: fix_unit_inserted added — emitted once a blocked RESULT's `blocked_next:` names a drafted fix unit that `insert_fix_unit` validated and inserted ahead of the blocked unit, carrying the fix unit's id, its file, the gate, and the insertion count; the blocked unit's own re-arm (an existing `re_arm_dispatched`, reason `fix_unit_inserted`) rides alongside it, so this is the only genuinely new event_type the mechanic needs.",
6
+ "$comment": "FEAT-2026-0060/T01: event_types derived from the union of every build_event(...) call site in specfuse/loop/loop.py and a corpus sweep of every .specfuse/features/*/events.jsonl, subtracting the vendored envelope enum. attempt_outcome and gate_reached were already-known precedent (loop.py's build_arm_predicate_event docstring); auto_close_decision, arm_predicate_evaluated, re_arm_dispatched, plan_next_draft_lint, and unsandboxed_dispatch are additional driver-local mechanics the orchestrator has no concept of. gate_auto_armed (loop.py build_event call under gate_events.append) and re_arm_rejected (loop.py build_event call under the spinning_reproduction_missing re-arm check) are emitted by real code paths that have never fired in a recorded run, so a corpus-only sweep cannot see them; they are included because call-site derivation does. FEAT-2026-0073/T01: correlation_id added — closing_names and hygiene_suffix_pattern mirror correlation-ids.md's documented shapes and lint_plan.py's CORRELATION_ID_RE verbatim, so the envelope, the rules file, and the PLAN-graph linter state one contract. FEAT-2026-0075/T03: driver_staleness_detected added — emitted at gate completion when a work unit's squash diff touched the driver's own importable surface, naming the editing unit, the driver paths it touched, and the IDs of units this process dispatched afterward (which ran the pre-edit module). FEAT-2026-0100/T02: judged added — emitted once per terminal close after the closing-deliverable guards, carrying the close's own verdict, the judge session's verdict (null when the judge was disabled, timed out, or returned nothing parseable, with the reason beside it), and whether the judge lowered it. FEAT-2026-0109/T01: baseline_attribution added — emitted when the retroactive per-attempt-failure baseline probe (attribute_failure_to_baseline) runs fresh, carrying the gate, the failing-gate set, and the unit whose failed attempt triggered the probe. FEAT-2026-0109/T06: broad_run_result added — emitted whenever the once-per-gate broad verification pass (gate_broad_run_check) actually runs (never on a tree-unchanged dedup skip), carrying the gate, the tree it was measured on, and its pass/fail outcome; a failing broad run additionally raises the existing human_escalation event type with reason: broad_run_gate_failure, which needs no schema addition since human_escalation is already sanctioned in the vendored envelope enum. FEAT-2026-0109/T08: driver_build_pinned added — emitted once per process, before any work unit is dispatched, when this process is itself a materialized pin (`_reexec_pinned`); carries the pin's tree hash and its path so an operator or test can read what build actually ran without inferring it from `ps` output. `driver_staleness_detected` needed no addition here: this unit reuses it with `halted: false` for the pinned, non-halting case (the registry constrains the type list, not the payload), adding `pinned_tree` / `next_pin_tree` fields alongside the existing `halted: true` shape. FEAT-2026-0104/T04: replan added — emitted once a re-plan turn (T03's run_replan_turn) rewrites a unit's body ahead of its last permitted attempt, so gate_eval.py's evaluate_auto_close (check 2, already reading this event_type since FEAT-2026-0018/T02) can disable auto-close for a gate that needed one. FEAT-2026-0115/T01: fix_unit_inserted added — emitted once a blocked RESULT's `blocked_next:` names a drafted fix unit that `insert_fix_unit` validated and inserted ahead of the blocked unit, carrying the fix unit's id, its file, the gate, and the insertion count; the blocked unit's own re-arm (an existing `re_arm_dispatched`, reason `fix_unit_inserted`) rides alongside it, so this is the only genuinely new event_type the mechanic needs. FEAT-2026-0246/T01: terminal_judge_editing_evaluated added — emitted on every terminal gate immediately before the close (auto or dispatched), carrying the gate, the `arm_eval.evaluate_terminal_judge_editing` verdict status and reason; a `fired` verdict additionally raises the existing human_escalation event type with reason: judge_editing_terminal, which needs no schema addition. FEAT-2026-0246/T02: terminal_judge_editing_acknowledged added — emitted when that check is clean only because the close unit carries an operator-authored judge_edit_acknowledged field covering every hit, carrying the gate, close unit id, acknowledged paths, and the operator's reason. FEAT-2026-0247/T06: the correlation-ID shape (legacy FEAT-YYYY-NNNN and the keyed FEAT-<KEY>-YYYY-NNNN form) now comes from specfuse.loop.ids.",
7
7
  "event_types": [
8
8
  "attempt_outcome",
9
9
  "gate_reached",
@@ -21,7 +21,9 @@
21
21
  "broad_run_result",
22
22
  "driver_build_pinned",
23
23
  "replan",
24
- "fix_unit_inserted"
24
+ "fix_unit_inserted",
25
+ "terminal_judge_editing_evaluated",
26
+ "terminal_judge_editing_acknowledged"
25
27
  ],
26
28
  "correlation_id": {
27
29
  "closing_names": [
@@ -24,7 +24,7 @@
24
24
  "correlation_id": {
25
25
  "type": "string",
26
26
  "description": "Correlation ID at any level of the unit hierarchy (docs/naming-convention.md): initiative INIT-YYYY-NNNN, dispatched feature INIT-YYYY-NNNN/FNN, work unit INIT-YYYY-NNNN/FNN/<task-id>; or the legacy/component-local feature FEAT-YYYY-NNNN and task FEAT-YYYY-NNNN/<task-id>. The task-id segment carries all three documented work-unit shapes: substantive TNN, hygiene TNNH[N...] (the target unit's ordinal, a literal H, and an optional ordinal), and closing-sequence G<n>-<NAME> where <NAME> is RETRO, LESSONS, DOCS, PLAN, CLOSE-INTERMEDIATE, or CLOSE. Existing FEAT- entries validate unchanged.",
27
- "pattern": "^(FEAT|INIT)-\\d{4}-\\d{4}(/F\\d{2})?(/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?$"
27
+ "pattern": "^(FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?|INIT-)\\d{4}-\\d{4}(/F\\d{2})?(/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE)))?$"
28
28
  },
29
29
  "event_type": {
30
30
  "type": "string",
@@ -15,7 +15,7 @@
15
15
  "original_issue_correlation_id": {
16
16
  "type": "string",
17
17
  "description": "The task-level correlation ID from the triggering task that filed the spec issue (e.g. `FEAT-2026-0042/T09`). Links the resolution event back to the original `spec_issue_raised` event and the downstream task that discovered the problem. This is the task-level ID, not the feature-level ID, because spec issues are raised in the context of a specific task's work. The task-id segment accepts every documented work-unit shape: substantive TNN, hygiene TNNH[N...], and closing-sequence G<n>-<NAME> (RETRO, LESSONS, DOCS, PLAN, CLOSE-INTERMEDIATE, CLOSE). See .specfuse/rules/correlation-ids.md.",
18
- "pattern": "^((FEAT-\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE))$"
18
+ "pattern": "^((FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE))$"
19
19
  },
20
20
  "affected_files": {
21
21
  "type": "array",
@@ -15,7 +15,7 @@
15
15
  "original_issue_correlation_id": {
16
16
  "type": "string",
17
17
  "description": "The task-level correlation ID from the triggering task that filed the spec issue (e.g. `FEAT-2026-0042/T09`). Links the routing event back to the original `spec_issue_raised` event and the downstream task that discovered the problem. The task-id segment accepts every documented work-unit shape: substantive TNN, hygiene TNNH[N...], and closing-sequence G<n>-<NAME> (RETRO, LESSONS, DOCS, PLAN, CLOSE-INTERMEDIATE, CLOSE). See .specfuse/rules/correlation-ids.md.",
18
- "pattern": "^((FEAT-\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE))$"
18
+ "pattern": "^((FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/(T\\d{2}(H\\d*)?|G\\d+-(RETRO|LESSONS|DOCS|PLAN|CLOSE-INTERMEDIATE|CLOSE))$"
19
19
  },
20
20
  "target_project": {
21
21
  "type": "string",
@@ -17,7 +17,7 @@
17
17
  "feature_correlation_id": {
18
18
  "type": "string",
19
19
  "description": "The feature-level correlation ID of the feature whose specs were validated. Duplicated from the envelope `correlation_id` for payload self-containment — downstream consumers that index payloads without re-reading the envelope benefit from the redundancy.",
20
- "pattern": "^(FEAT|INIT)-\\d{4}-\\d{4}(/F\\d{2})?$"
20
+ "pattern": "^(FEAT-(?:[A-Z][A-Z0-9]{1,5}-)?|INIT-)\\d{4}-\\d{4}(/F\\d{2})?$"
21
21
  },
22
22
  "pass": {
23
23
  "type": "boolean",
@@ -23,6 +23,7 @@ from dataclasses import dataclass
23
23
  from pathlib import Path
24
24
 
25
25
  from . import _miniyaml
26
+ from . import ids as _ids
26
27
 
27
28
  PREDICATE_VERSION = "v1"
28
29
  PER_WU_COST_RATIO_CEILING = 1.5
@@ -441,6 +442,7 @@ def _resolve_feature_dir(feature_id: str, repo_root: Path) -> "Path | None":
441
442
  return None
442
443
 
443
444
  candidates = sorted(d for d in features_dir.iterdir() if d.is_dir())
445
+ feature_id = _ids.resolve(repo_root, feature_id)
444
446
 
445
447
  # Priority 1: exact directory name
446
448
  exact = [d for d in candidates if d.name == feature_id]
@@ -458,8 +460,16 @@ def _resolve_feature_dir(feature_id: str, repo_root: Path) -> "Path | None":
458
460
  partial: list[Path] = []
459
461
  for d in candidates:
460
462
  name = d.name
463
+ parsed = _ids.parse(name)
464
+ if parsed is not None:
465
+ # FEAT-[KEY-]YYYY-NNNN-slug: compare the ordinal and the slug
466
+ head = str(parsed)
467
+ nnnn = f"{parsed.ordinal:04d}"
468
+ slug = name[len(head) + 1:]
469
+ if nnnn == feature_id or slug == feature_id:
470
+ partial.append(d)
471
+ continue
461
472
  parts = name.split("-")
462
- # FEAT-YYYY-NNNN-slug → parts[2] is the NNNN part
463
473
  if len(parts) >= 4:
464
474
  nnnn = parts[2]
465
475
  slug = "-".join(parts[3:])