specfuse-loop 0.16.0__tar.gz → 0.18.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (546) hide show
  1. {specfuse_loop-0.16.0/specfuse_loop.egg-info → specfuse_loop-0.18.0}/PKG-INFO +1 -1
  2. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/pyproject.toml +14 -1
  3. specfuse_loop-0.18.0/specfuse/loop/build_provenance.py +332 -0
  4. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/closing_requirements.py +30 -2
  5. specfuse_loop-0.18.0/specfuse/loop/data/VERSION +1 -0
  6. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/methodology.md +30 -6
  7. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/close-discipline.md +23 -1
  8. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/operator-escalation.md +4 -4
  9. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/result-contract.md +16 -5
  10. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/verification-discipline.md +6 -3
  11. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/schemas/driver-event.schema.json +5 -2
  12. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/templates/GATE.template.md +4 -0
  13. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/templates/WU.template.md +3 -3
  14. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/verification.yml.example +23 -0
  15. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/escalation.py +13 -6
  16. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/lint_closing.py +41 -1
  17. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/lint_plan.py +172 -0
  18. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/loop.py +1658 -130
  19. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
  20. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse_loop.egg-info/SOURCES.txt +19 -0
  21. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_wiring.py +38 -14
  22. specfuse_loop-0.18.0/tests/test_attempt_note_untracked_files.py +126 -0
  23. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_baseline_escalation_message.py +6 -1
  24. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_baseline_persistence.py +86 -35
  25. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_baseline_probe.py +6 -66
  26. specfuse_loop-0.18.0/tests/test_baseline_provenance.py +135 -0
  27. specfuse_loop-0.18.0/tests/test_baseline_tree_hash_key.py +173 -0
  28. specfuse_loop-0.18.0/tests/test_changed_file_test_selection.py +207 -0
  29. specfuse_loop-0.18.0/tests/test_close_records_feature_oracle.py +142 -0
  30. specfuse_loop-0.18.0/tests/test_criteria_per_gate_lint.py +161 -0
  31. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_restart_halt_wiring.py +22 -0
  32. specfuse_loop-0.18.0/tests/test_feature_oracle_e2e.py +110 -0
  33. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_followups_artifact.py +160 -8
  34. specfuse_loop-0.18.0/tests/test_gate_broad_run.py +475 -0
  35. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_human_work_unit.py +4 -1
  36. specfuse_loop-0.18.0/tests/test_installed_copy_driver_e2e.py +450 -0
  37. specfuse_loop-0.18.0/tests/test_lazy_baseline_e2e.py +509 -0
  38. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_bare_produces_path.py +3 -1
  39. specfuse_loop-0.18.0/tests/test_lint_feature_oracle_declared.py +271 -0
  40. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_planning_sections.py +2 -1
  41. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_produces_driver_helper.py +1 -0
  42. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_criteria_skeleton.py +6 -0
  43. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_gate_budget.py +2 -1
  44. specfuse_loop-0.18.0/tests/test_oracle_unrunnable_attribution.py +116 -0
  45. specfuse_loop-0.18.0/tests/test_pin_honesty_and_integrity.py +245 -0
  46. specfuse_loop-0.18.0/tests/test_pin_marker_not_inherited.py +99 -0
  47. specfuse_loop-0.18.0/tests/test_pin_marker_spawn_sites.py +128 -0
  48. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_field.py +1 -0
  49. specfuse_loop-0.18.0/tests/test_produces_justification.py +324 -0
  50. specfuse_loop-0.18.0/tests/test_rearm_clears_stale_criteria_state.py +143 -0
  51. specfuse_loop-0.18.0/tests/test_red_baseline_never_reused.py +268 -0
  52. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_doc_hygiene.py +44 -0
  53. specfuse_loop-0.18.0/tests/test_tiered_verification_e2e.py +366 -0
  54. specfuse_loop-0.16.0/specfuse/loop/build_provenance.py +0 -106
  55. specfuse_loop-0.16.0/specfuse/loop/data/VERSION +0 -1
  56. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/LICENSE +0 -0
  57. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/NOTICE +0 -0
  58. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/README.md +0 -0
  59. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/setup.cfg +0 -0
  60. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/__init__.py +0 -0
  61. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/budget.py +0 -0
  62. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/diagnose_invoke.py +0 -0
  63. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/drafting_answers.py +0 -0
  64. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/drafting_invoke.py +0 -0
  65. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/drafting_questions.py +0 -0
  66. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/driver_command.py +0 -0
  67. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/driver_invoke.py +0 -0
  68. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/invoke.py +0 -0
  69. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/monitoring_read.py +0 -0
  70. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/__init__.py +0 -0
  71. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/answers.py +0 -0
  72. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/bugs.py +0 -0
  73. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/feature.py +0 -0
  74. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/findings_autofix.py +0 -0
  75. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/findings_diagnose.py +0 -0
  76. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/providers/triage.py +0 -0
  77. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/queue_read.py +0 -0
  78. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/repo_detect.py +0 -0
  79. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/run.py +0 -0
  80. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/state.py +0 -0
  81. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/triage_invoke.py +0 -0
  82. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/agent/worktree.py +0 -0
  83. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/__init__.py +0 -0
  84. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/_filelock.py +0 -0
  85. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/_miniyaml.py +0 -0
  86. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/_wu_sections.py +0 -0
  87. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/adopt_feature.py +0 -0
  88. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/agent_policy.py +0 -0
  89. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/arm_eval.py +0 -0
  90. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/arm_sweep.py +0 -0
  91. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/arm_txn.py +0 -0
  92. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/bug_lane.py +0 -0
  93. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/bug_lane_run.py +0 -0
  94. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/bug_lane_state.py +0 -0
  95. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/changelog.py +0 -0
  96. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/cost.py +0 -0
  97. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/criteria_state.py +0 -0
  98. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/CHANGELOG.seed.md +0 -0
  99. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  100. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/concepts/adopting-auto-mode.md +0 -0
  101. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  102. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/concepts/autonomy-stop-classes.md +0 -0
  103. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  104. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/getting-started.md +0 -0
  105. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/glossary.md +0 -0
  106. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/docs/skills.md +0 -0
  107. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/gitignore.snippet +0 -0
  108. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/monitoring-secrets-checklist.md +0 -0
  109. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/monitoring.overrides.yml.example +0 -0
  110. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/monitoring.yml.example +0 -0
  111. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/roadmap.template.md +0 -0
  112. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/correlation-ids.md +0 -0
  113. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/design-for-diagnosis.md +0 -0
  114. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/human-output.md +0 -0
  115. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/never-touch.md +0 -0
  116. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/planning-discipline.md +0 -0
  117. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules/security-boundaries.md +0 -0
  118. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/rules-local/README.md +0 -0
  119. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/schemas/event.schema.json +0 -0
  120. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/schemas/events/initiative_created.schema.json +0 -0
  121. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +0 -0
  122. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +0 -0
  123. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/schemas/events/spec_validated.schema.json +0 -0
  124. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/templates/DECISIONS.template.md +0 -0
  125. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/templates/LEARNINGS-pending.template.md +0 -0
  126. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  127. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/data/workflows/specfuse-monitor.yml +0 -0
  128. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/decisions_format.py +0 -0
  129. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/driver_edit.py +0 -0
  130. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/events_stats.py +0 -0
  131. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/gate_commands.py +0 -0
  132. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/gate_eval.py +0 -0
  133. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/gh_backend.py +0 -0
  134. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/gh_features.py +0 -0
  135. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/heartbeat.py +0 -0
  136. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/judge.py +0 -0
  137. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/labels.py +0 -0
  138. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/learnings_query.py +0 -0
  139. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/lint_monitoring.py +0 -0
  140. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/lint_roadmap.py +0 -0
  141. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/notify.py +0 -0
  142. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/notify_escalation.py +0 -0
  143. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/notify_sla.py +0 -0
  144. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/plan_baseline.py +0 -0
  145. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/policy_proposals.py +0 -0
  146. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/policy_review.py +0 -0
  147. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/prerun.py +0 -0
  148. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/prerun_capture.py +0 -0
  149. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/promotion.py +0 -0
  150. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/rearm_migration.py +0 -0
  151. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/replay_spin.py +0 -0
  152. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/roadmap_archive.py +0 -0
  153. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/scaffold.py +0 -0
  154. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/triage.py +0 -0
  155. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/upgrade_merge_gate.py +0 -0
  156. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/loop/validate_event.py +0 -0
  157. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/__init__.py +0 -0
  158. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/adapters.py +0 -0
  159. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/artifact.py +0 -0
  160. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/autofix.py +0 -0
  161. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/autofix_invoke.py +0 -0
  162. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/autofix_run.py +0 -0
  163. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/autofix_state.py +0 -0
  164. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/cli.py +0 -0
  165. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/diagnose_cli.py +0 -0
  166. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/diagnosis.py +0 -0
  167. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/fingerprint.py +0 -0
  168. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/issues.py +0 -0
  169. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/providers/__init__.py +0 -0
  170. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/providers/_azure_auth.py +0 -0
  171. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/providers/azure_app_insights.py +0 -0
  172. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/providers/azure_service_bus.py +0 -0
  173. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/redaction.py +0 -0
  174. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse/monitor/schedule.py +0 -0
  175. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  176. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
  177. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse_loop.egg-info/requires.txt +0 -0
  178. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/specfuse_loop.egg-info/top_level.txt +0 -0
  179. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_abandon_feature_skill.py +0 -0
  180. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_abandoned_work_reported.py +0 -0
  181. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_abandoned_wu_strands_gate.py +0 -0
  182. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_adopt_feature.py +0 -0
  183. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_budget.py +0 -0
  184. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_bug_selection_dedup.py +0 -0
  185. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_default_features_root.py +0 -0
  186. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_driver_command.py +0 -0
  187. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_driver_invoke.py +0 -0
  188. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_driver_restart.py +0 -0
  189. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_escalation_on_issue.py +0 -0
  190. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_escalation_run_state.py +0 -0
  191. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_findings_seam.py +0 -0
  192. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_invoke_timeout.py +0 -0
  193. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_invoke_usage.py +0 -0
  194. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_item_worktree.py +0 -0
  195. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_lock.py +0 -0
  196. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_policy_key_ownership.py +0 -0
  197. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_policy_queue.py +0 -0
  198. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_policy_schema.py +0 -0
  199. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_policy_triage_dial.py +0 -0
  200. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_answers.py +0 -0
  201. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_bugs.py +0 -0
  202. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_feature.py +0 -0
  203. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_findings_autofix.py +0 -0
  204. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_findings_diagnose.py +0 -0
  205. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_isolation.py +0 -0
  206. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_provider_triage.py +0 -0
  207. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_queue_read.py +0 -0
  208. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_relays_driver_output.py +0 -0
  209. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_repo_detect.py +0 -0
  210. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_run.py +0 -0
  211. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_run_progress.py +0 -0
  212. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_seam.py +0 -0
  213. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_agent_state.py +0 -0
  214. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_answer_escalation_skill.py +0 -0
  215. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_app_insights_adapters.py +0 -0
  216. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_archive_ref_reconciliation.py +0 -0
  217. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_eval.py +0 -0
  218. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_eval_lint_class.py +0 -0
  219. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_eval_wiring.py +0 -0
  220. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  221. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_sweep.py +0 -0
  222. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_sweep_gate.py +0 -0
  223. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_arm_txn.py +0 -0
  224. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_artifact_redaction.py +0 -0
  225. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_attempt_outcome_contract.py +0 -0
  226. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_attempt_outcome_emission.py +0 -0
  227. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_attention_nonwriting_guard.py +0 -0
  228. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_attention_silence_section.py +0 -0
  229. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_attention_skill_structure.py +0 -0
  230. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autoclose_deferral_visibility.py +0 -0
  231. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autoclose_gate_note.py +0 -0
  232. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autoclose_stub_states_what_passed.py +0 -0
  233. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autofix_invoke.py +0 -0
  234. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autofix_live.py +0 -0
  235. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autofix_predicate.py +0 -0
  236. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autofix_run.py +0 -0
  237. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autofix_state.py +0 -0
  238. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autosync.py +0 -0
  239. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autosync_consent.py +0 -0
  240. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autosync_firstrun.py +0 -0
  241. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autosync_no_cwd_leak.py +0 -0
  242. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_autosync_plugin.py +0 -0
  243. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_azure_credential_resolution.py +0 -0
  244. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_backend.py +0 -0
  245. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bash_routing.py +0 -0
  246. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bats_suites_gated.py +0 -0
  247. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_binary_verdict.py +0 -0
  248. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  249. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  250. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bookkeeping_no_verify.py +0 -0
  251. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_ci_pending.py +0 -0
  252. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_guardrails.py +0 -0
  253. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_label_nonfatal.py +0 -0
  254. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_labels_registered.py +0 -0
  255. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_policy_contract.py +0 -0
  256. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_pr_number_carried.py +0 -0
  257. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_preflight_and_dial.py +0 -0
  258. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_run.py +0 -0
  259. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_state.py +0 -0
  260. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_stopped_outcome_pr_number.py +0 -0
  261. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_bug_lane_test_paths.py +0 -0
  262. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_build_provenance.py +0 -0
  263. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_changelog_append_only.py +0 -0
  264. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_changelog_collection.py +0 -0
  265. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_changelog_release_wiring.py +0 -0
  266. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_changelog_schema.py +0 -0
  267. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_ci_conclusion_wait.py +0 -0
  268. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_ci_runs_every_gate.py +0 -0
  269. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_claude_resolution.py +0 -0
  270. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_close_b_pending_learnings.py +0 -0
  271. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_close_wu_self_reference.py +0 -0
  272. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_closing_deliverable_guard.py +0 -0
  273. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_closing_guard_contracts.py +0 -0
  274. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_closing_guard_prediction.py +0 -0
  275. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_closing_learnings_destination.py +0 -0
  276. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_closing_requirements.py +0 -0
  277. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_coauthor_trailer_allowlist.py +0 -0
  278. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_console_encoding.py +0 -0
  279. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_convergent_iteration.py +0 -0
  280. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_convergent_wu_lint.py +0 -0
  281. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_correlation_id_override.py +0 -0
  282. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_correlation_id_two_surfaces.py +0 -0
  283. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_cost_lifetime.py +0 -0
  284. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_cost_tracking.py +0 -0
  285. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_criteria_state.py +0 -0
  286. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_criteria_worklist.py +0 -0
  287. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_decision_citation_lint.py +0 -0
  288. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_decision_followups_discharged.py +0 -0
  289. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_decision_override_lint.py +0 -0
  290. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_decisions_format.py +0 -0
  291. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_degraded_oracle.py +0 -0
  292. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_deliverable_missing_note.py +0 -0
  293. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_deliverable_presence_gate.py +0 -0
  294. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_derive_agent_policy_review_mode.py +0 -0
  295. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_derive_agent_policy_skill.py +0 -0
  296. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_derive_monitoring_discovery.py +0 -0
  297. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_derive_monitoring_skill_registration.py +0 -0
  298. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_design_for_diagnosis_rule.py +0 -0
  299. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_deterministic_refusal_repeat.py +0 -0
  300. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_diagnose_headless.py +0 -0
  301. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_diagnose_issue_skill.py +0 -0
  302. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_diagnosis.py +0 -0
  303. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_diagnosis_roundtrip_live.py +0 -0
  304. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_dispatch_skeleton.py +0 -0
  305. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_doctor.py +0 -0
  306. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_done_feature_gates.py +0 -0
  307. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_draft_feature_answers_mode.py +0 -0
  308. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_drafting_answer_gate.py +0 -0
  309. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_drafting_invoke.py +0 -0
  310. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_drafting_question_issue.py +0 -0
  311. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_drafting_questions.py +0 -0
  312. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_drafting_reply_shape.py +0 -0
  313. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_edit_detection.py +0 -0
  314. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_event_registry_covers_emitters.py +0 -0
  315. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_integration.py +0 -0
  316. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_lock.py +0 -0
  317. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_module_surface.py +0 -0
  318. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_restart_halt_surfaced.py +0 -0
  319. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_restart_hold.py +0 -0
  320. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_staleness_gate_summary.py +0 -0
  321. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_driver_staleness_warning.py +0 -0
  322. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_dry_run_no_branch_mutation.py +0 -0
  323. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_duration_tracking.py +0 -0
  324. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_empty_files_escalation.py +0 -0
  325. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_ensure_feature_branch.py +0 -0
  326. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_ensure_feature_branch_base.py +0 -0
  327. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_escalation_assignee_policy.py +0 -0
  328. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_escalation_contract.py +0 -0
  329. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_escalation_crash_containment.py +0 -0
  330. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_escalation_dedup_search.py +0 -0
  331. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_escalation_emit.py +0 -0
  332. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_event_gate_full_envelope.py +0 -0
  333. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_events_redaction.py +0 -0
  334. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_events_stats.py +0 -0
  335. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_extra_gates.py +0 -0
  336. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_failure_artifact_model.py +0 -0
  337. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_failure_signature_fail_prefix.py +0 -0
  338. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_failure_signature_skips_log_noise.py +0 -0
  339. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_feature_branch_auto_sync.py +0 -0
  340. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_feature_branch_squash_merge.py +0 -0
  341. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_feature_branch_stale_base.py +0 -0
  342. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_feature_id_resolution.py +0 -0
  343. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_feature_provider_drafting_dispatch.py +0 -0
  344. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_feature_review_accumulation.py +0 -0
  345. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_filelock_portable.py +0 -0
  346. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_find_pr_no_search_index.py +0 -0
  347. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_fingerprint.py +0 -0
  348. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_fix_bug_diff_self_check.py +0 -0
  349. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_fix_bug_headless.py +0 -0
  350. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_fix_bug_reads_comments.py +0 -0
  351. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_fold_contract_documented.py +0 -0
  352. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_force_full_close.py +0 -0
  353. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_frontmatter_scalar_quoting.py +0 -0
  354. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_budget.py +0 -0
  355. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_budget_post_dispatch.py +0 -0
  356. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_commands_needs.py +0 -0
  357. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_eval.py +0 -0
  358. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_eval_calibration.py +0 -0
  359. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  360. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_eval_repo_flag.py +0 -0
  361. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_eval_terminal_wiring.py +0 -0
  362. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gate_report_verdict.py +0 -0
  363. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gh_backend.py +0 -0
  364. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gh_features.py +0 -0
  365. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_git_env_isolation.py +0 -0
  366. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_gitleaks_pinning.py +0 -0
  367. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_groom_backlog_skill.py +0 -0
  368. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_guard_refusal_failure_class.py +0 -0
  369. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_guard_refusal_failure_excerpt.py +0 -0
  370. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_hashed_denylist.py +0 -0
  371. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_hashed_denylist_ci.py +0 -0
  372. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_heartbeat.py +0 -0
  373. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_heartbeat_adapter.py +0 -0
  374. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_human_output_rule.py +0 -0
  375. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_hyphenated_gate_name.py +0 -0
  376. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_init_integration.py +0 -0
  377. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_interpreter_normalization.py +0 -0
  378. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_judge_close_path.py +0 -0
  379. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_judge_cost.py +0 -0
  380. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_judge_module.py +0 -0
  381. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_judge_path_registry.py +0 -0
  382. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_label_provisioning_runner_contract.py +0 -0
  383. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_label_registry.py +0 -0
  384. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_label_registry_covers_consumers.py +0 -0
  385. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_leak_findings_redaction.py +0 -0
  386. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_leak_scan.py +0 -0
  387. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_leak_scan_content.py +0 -0
  388. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_learnings_curate_vendored_promote.py +0 -0
  389. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_learnings_query.py +0 -0
  390. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_learnings_staging.py +0 -0
  391. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_learnings_suggest_signature_filter.py +0 -0
  392. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  393. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lifecycle_integration.py +0 -0
  394. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lifetime_accounting.py +0 -0
  395. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_ac_observable.py +0 -0
  396. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_boundary_consistency.py +0 -0
  397. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_boundary_extraction.py +0 -0
  398. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_close_intermediate.py +0 -0
  399. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_close_verdict_not_predecided.py +0 -0
  400. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_close_wu.py +0 -0
  401. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_closing.py +0 -0
  402. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_closing_criteria.py +0 -0
  403. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_closing_criteria_pristine.py +0 -0
  404. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_correlation_id.py +0 -0
  405. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  406. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_gate_proportionality.py +0 -0
  407. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_monitoring.py +0 -0
  408. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_oracle_env.py +0 -0
  409. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_plan_contract_fields.py +0 -0
  410. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_plan_errors.py +0 -0
  411. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_plan_next_draft.py +0 -0
  412. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_plan_relative_import.py +0 -0
  413. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_plan_verdict_exempt.py +0 -0
  414. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_produces_satisfiability.py +0 -0
  415. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_roadmap.py +0 -0
  416. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_sections.py +0 -0
  417. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  418. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_lint_verification_reference.py +0 -0
  419. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_live_tests_opt_in.py +0 -0
  420. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_load_bearing_close_lint.py +0 -0
  421. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_attempt_evidence.py +0 -0
  422. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_auto_archive.py +0 -0
  423. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_caveman_preamble.py +0 -0
  424. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_close_intermediate.py +0 -0
  425. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_criteria_survival.py +0 -0
  426. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_defaults_by_type.py +0 -0
  427. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_effort.py +0 -0
  428. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_failure_note_cap.py +0 -0
  429. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_files_changed_guard.py +0 -0
  430. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_gate_needs.py +0 -0
  431. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_model_alias.py +0 -0
  432. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_orchestration.py +0 -0
  433. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_post_pass_invariant.py +0 -0
  434. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_reset_preserving_events.py +0 -0
  435. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_smoke_runner.py +0 -0
  436. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_unsandboxed.py +0 -0
  437. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_worklist_injection.py +0 -0
  438. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_loop_zero_token_guard.py +0 -0
  439. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_make_repo_gpgsign_isolation.py +0 -0
  440. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_max_attempts_configurable.py +0 -0
  441. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_migrate_legacy.py +0 -0
  442. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_miniyaml_equivalence.py +0 -0
  443. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_miniyaml_negative.py +0 -0
  444. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitor_cli.py +0 -0
  445. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitor_issue_lifecycle.py +0 -0
  446. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitor_runner_surfaces.py +0 -0
  447. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitoring_bootstrap_artifacts.py +0 -0
  448. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitoring_cron_dialect.py +0 -0
  449. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitoring_example.py +0 -0
  450. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitoring_fenced_blocks.py +0 -0
  451. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_monitoring_seed.py +0 -0
  452. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_no_pytest_subprocess.py +0 -0
  453. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_notify.py +0 -0
  454. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_notify_escalation.py +0 -0
  455. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_notify_sla.py +0 -0
  456. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_operator_escalation_rule.py +0 -0
  457. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_oracle_set_declared.py +0 -0
  458. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_pick_feature_skill.py +0 -0
  459. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_plan_baseline.py +0 -0
  460. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_planned_cost_lint.py +0 -0
  461. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_planning_cost_floor.py +0 -0
  462. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_policy_proposals.py +0 -0
  463. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_policy_review.py +0 -0
  464. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_pr_checks_json_field.py +0 -0
  465. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_pre_existing_claims_cite_evidence.py +0 -0
  466. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_prep_halt_operator_surface.py +0 -0
  467. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_prepare_feature.py +0 -0
  468. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_prepare_scaffold_sync.py +0 -0
  469. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_prerun_capture.py +0 -0
  470. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_prerun_oracles.py +0 -0
  471. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_prerun_wiring.py +0 -0
  472. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_bracket_literal.py +0 -0
  473. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_bracket_wildcard.py +0 -0
  474. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_in_diff.py +0 -0
  475. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_recursive_glob.py +0 -0
  476. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_semantics_unified.py +0 -0
  477. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_produces_shape.py +0 -0
  478. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_promotion_ledger.py +0 -0
  479. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_provider_escalation_traces.py +0 -0
  480. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_provision_labels.py +0 -0
  481. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_queue_stalled_adapter.py +0 -0
  482. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_rearm_fold_marker.py +0 -0
  483. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_rearm_migration.py +0 -0
  484. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_redaction_windows_home.py +0 -0
  485. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_replay_spin.py +0 -0
  486. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_resolve_base.py +0 -0
  487. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_restart_cap_counts_progress.py +0 -0
  488. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_result_block.py +0 -0
  489. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_result_block_audience.py +0 -0
  490. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_resume_command_build.py +0 -0
  491. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_add_skill.py +0 -0
  492. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_archive_cli.py +0 -0
  493. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_archive_shim.py +0 -0
  494. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_archive_skill.py +0 -0
  495. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_archive_skill_delegates.py +0 -0
  496. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_link_gate.py +0 -0
  497. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_roadmap_row_parser.py +0 -0
  498. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_ruff_verdict_lines.py +0 -0
  499. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_data_in_sync.py +0 -0
  500. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_docs.py +0 -0
  501. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_init.py +0 -0
  502. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_label_provisioning.py +0 -0
  503. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_manifest.py +0 -0
  504. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_resources.py +0 -0
  505. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_seed_sanity.py +0 -0
  506. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_skew_halts.py +0 -0
  507. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_upgrade.py +0 -0
  508. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_scaffold_wiring.py +0 -0
  509. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_schedule_dialect.py +0 -0
  510. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_seed_documents_prerun_keys.py +0 -0
  511. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_service_bus_dlq_adapter.py +0 -0
  512. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_session_env_preflight.py +0 -0
  513. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_shipped_templates_parse.py +0 -0
  514. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_skill_discovery_links.py +0 -0
  515. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_skills_vendored_in_sync.py +0 -0
  516. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_spinning_rearm_gate.py +0 -0
  517. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_squash_commit_hook_crash.py +0 -0
  518. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_stop_rationale_captured.py +0 -0
  519. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_surefire_run_level_signature.py +0 -0
  520. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_surefire_verdict_lines.py +0 -0
  521. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_template_closing_shapes.py +0 -0
  522. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_terminal_flip_ownership.py +0 -0
  523. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_terminal_flips.py +0 -0
  524. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_terminal_gate_message.py +0 -0
  525. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_timeout_kill_portable.py +0 -0
  526. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_triage.py +0 -0
  527. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_triage_apply.py +0 -0
  528. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_triage_skill_contract.py +0 -0
  529. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_triage_skips_agent_escalations.py +0 -0
  530. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_tsc_verdict_lines.py +0 -0
  531. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_umbrella_version_resolution.py +0 -0
  532. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_unterminated_frontmatter.py +0 -0
  533. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_untracked_feature_folder.py +0 -0
  534. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_untracked_file_absorption.py +0 -0
  535. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_upgrade_integration.py +0 -0
  536. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_upgrade_merge_gate.py +0 -0
  537. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_validate_event.py +0 -0
  538. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_validate_event_driver_types.py +0 -0
  539. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_verdict_coupling.py +0 -0
  540. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_verify_empty_gate_set.py +0 -0
  541. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_version_consistency.py +0 -0
  542. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_version_skew.py +0 -0
  543. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_windows_gate_exec.py +0 -0
  544. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_worktree_restore.py +0 -0
  545. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_wu_execution_metadata.py +0 -0
  546. {specfuse_loop-0.16.0 → specfuse_loop-0.18.0}/tests/test_wu_template_renders_lintable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.16.0
3
+ Version: 0.18.0
4
4
  Summary: Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology.
5
5
  Author: Specfuse contributors
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "specfuse-loop"
7
- version = "0.16.0"
7
+ version = "0.18.0"
8
8
  description = "Local-first executor for the Specfuse Plan + Work Unit gate-cycle methodology."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -88,3 +88,16 @@ namespaces = true
88
88
  # templates, rules, and other seed files out of the wheel without needing the
89
89
  # loop source repo on disk.
90
90
  "specfuse.loop" = ["data/**/*", "data/*"]
91
+
92
+ # FEAT-2026-0109/T05: per-test dynamic contexts turn the `tests` gate's
93
+ # existing `coverage run --source=specfuse` (`.specfuse/verification.yml`)
94
+ # into a measured file/line -> test map for free — see
95
+ # `GATE-02-REVIEW.md` § "Tests touching changed files" for why this beat an
96
+ # import-graph or path-convention rule. Lives here rather than a new
97
+ # `.coveragerc` because none exists today and the `tests` gate already
98
+ # passes `--source` on the command line; adding a second config surface
99
+ # would split coverage configuration in two. Read by `build_changed_file_test_map`
100
+ # (`specfuse/loop/loop.py`) via the `coverage` package's own config loading —
101
+ # no code here parses this section directly.
102
+ [tool.coverage.run]
103
+ dynamic_context = "test_function"
@@ -0,0 +1,332 @@
1
+ # Copyright 2026 Specfuse contributors
2
+ # Licensed under the Apache License, Version 2.0. See LICENSE.
3
+ """Which build am I actually running? (#1040)
4
+
5
+ An installed console script (`specfuse-loop`, `specfuse-lint`, …) resolves
6
+ `specfuse.loop` from `site-packages`. In a checkout of the driver's own source
7
+ whose tree has moved ahead of the installed wheel, that means the command runs
8
+ **a different program than the session believes it is running** -- and reports
9
+ confidently.
10
+
11
+ The failure mode is the worst kind: it does not error, it returns a plausible
12
+ number. Three observed occurrences, two at real cost:
13
+
14
+ * A gate-1 arming probe swept `specfuse-lint --closing` to confirm a new
15
+ requirement added no findings. It resolved to the installed 0.7.1 wheel,
16
+ which contained neither the new rules nor the module implementing them --
17
+ "the sweep would have reported identically had T03 shipped nothing."
18
+ * A terminal close produced **14 spurious red results** before the run was
19
+ repeated from the repo root.
20
+
21
+ Every affected surface had been patched by convention -- work-unit bodies
22
+ saying "use the `.specfuse/scripts/` shim, not the installed console script"
23
+ -- which is exactly the shape `a-rule-a-human-must-execute-is-not-a-control`
24
+ says does not hold: a rule a session must remember, a silent wrong answer when
25
+ it forgets, and no signal telling the two apart.
26
+
27
+ This module is that signal. It warns; it does not refuse. #1040 lists refusal
28
+ as a stronger option and recommends warning first, on the grounds that the
29
+ warning's own evidence is what tells you whether refusal is needed.
30
+
31
+ **Silent for downstream projects by construction.** The check fires only when
32
+ the directory being operated on contains a `specfuse/loop/` source tree of its
33
+ own -- i.e. a checkout of the driver. A project that installed Specfuse has no
34
+ such path, so the console script is the correct thing to run there and this
35
+ module never says anything.
36
+
37
+ **A third state (FEAT-2026-0109/T08).** An out-of-tree build used to be
38
+ uniformly "confidently wrong" -- an arbitrary installed wheel, indistinguishable
39
+ from a driver that forgot to rebuild. It no longer is: `main()` now
40
+ materializes its own pin of `specfuse/` outside the working tree, keyed on
41
+ `HEAD^{tree}`, and re-enters execution from it (`loop.py`'s `_reexec_pinned`).
42
+ That pin's provenance is recorded (`_PIN_MARKER_NAME`), so `out_of_tree_warning`
43
+ can tell the two apart: a *recorded* pin reports both tree hashes and says
44
+ which one it executed; an *unidentified* out-of-tree build keeps the alarming
45
+ text verbatim, because it is still exactly as dangerous as the wheel that
46
+ motivated this module.
47
+ """
48
+
49
+ from __future__ import annotations
50
+
51
+ import os
52
+ import shutil
53
+ import subprocess
54
+ import sys
55
+ import tempfile
56
+ from pathlib import Path
57
+ from typing import Optional
58
+
59
+ #: The shim that always resolves to the working tree, named in the warning as
60
+ #: the concrete fix. `.specfuse/scripts/` is vendored per project.
61
+ _SHIM_HINT = "python3 -m specfuse.loop.loop (or .specfuse/scripts/<script>.py)"
62
+
63
+ #: Relative path identifying a driver source checkout.
64
+ _SOURCE_MARKER = Path("specfuse") / "loop" / "loop.py"
65
+
66
+
67
+ def running_package_dir() -> Path:
68
+ """The `specfuse/loop/` directory this process actually imported.
69
+
70
+ Derived from this module's own location rather than by importing
71
+ `loop.py`: the check runs at CLI startup, and importing the driver to ask
72
+ where the driver is would be both slow and circular.
73
+ """
74
+ return Path(__file__).resolve().parent
75
+
76
+
77
+ def source_tree_package_dir(start: Optional[Path] = None) -> Optional[Path]:
78
+ """The `specfuse/loop/` of the source checkout *start* sits in, if any.
79
+
80
+ Walks upward from *start* (default: the current directory) looking for a
81
+ `specfuse/loop/loop.py`. Returns `None` when there is none -- the normal
82
+ case for a project that installed Specfuse, and the reason this check is
83
+ silent there.
84
+ """
85
+ current = (start if start is not None else Path.cwd()).resolve()
86
+ for candidate in (current, *current.parents):
87
+ if (candidate / _SOURCE_MARKER).is_file():
88
+ return (candidate / "specfuse" / "loop").resolve()
89
+ return None
90
+
91
+
92
+ #: Repo root, relative to which the pin cache lives -- overridable so tests
93
+ #: (and an operator with no writable platform cache) can redirect it without
94
+ #: env-var-shaped magic sprinkled through the pinning logic itself.
95
+ _PIN_CACHE_ENV_VAR = "SPECFUSE_PIN_CACHE_DIR"
96
+
97
+ #: Marker file written into a materialized pin, naming the tree hash it was
98
+ #: built from. Its presence (and matching content) is what lets
99
+ #: `out_of_tree_warning` tell a recorded pin apart from an arbitrary
100
+ #: out-of-tree build -- anything under the pin cache root WITHOUT this marker
101
+ #: (or whose content disagrees with its own directory name) is treated as
102
+ #: unidentified, never trusted silently.
103
+ _PIN_MARKER_NAME = ".specfuse-pin-tree"
104
+
105
+ #: Manifest of every file `materialize_pin` copied into a pin, written
106
+ #: alongside the marker. The marker alone only proves a directory CLAIMS to be
107
+ #: the pin for a tree hash; an age-based tmp reaper (or anything else) can
108
+ #: delete files from under it without touching the marker, and a copy missing
109
+ #: files must never be reported as the running build (FEAT-2026-0109/T09,
110
+ #: FOLLOW-UPS.md "A materialized pin can lose its files ..."). Reuse checks
111
+ #: every path this manifest lists before trusting an existing pin.
112
+ _PIN_MANIFEST_NAME = ".specfuse-pin-manifest"
113
+
114
+ #: The launcher `main()` re-execs through once a pin is materialized. Its own
115
+ #: directory becomes `sys.path[0]` for a script invocation (probed fact #2 in
116
+ #: FEAT-2026-0109/T08's work-unit body), so it lives beside the pinned
117
+ #: `specfuse/` copy rather than importing it via `-m` or `-c`, both of which
118
+ #: the probe showed the working tree's copy shadows.
119
+ PIN_LAUNCHER_NAME = "_run_pinned.py"
120
+
121
+ _PIN_LAUNCHER_SOURCE = '''\
122
+ """Generated by specfuse.loop.build_provenance.materialize_pin — do not edit.
123
+
124
+ Re-enters `specfuse.loop.loop:main` from this pin. This file's own directory
125
+ is `sys.path[0]` for a script invocation, ahead of the cwd, so the `specfuse`
126
+ package importable next to it resolves before the working tree's copy does.
127
+ """
128
+ import sys
129
+ from pathlib import Path
130
+
131
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
132
+
133
+ from specfuse.loop.loop import main # noqa: E402
134
+
135
+ if __name__ == "__main__":
136
+ sys.exit(main())
137
+ '''
138
+
139
+
140
+ def repo_root_for(start: Optional[Path] = None) -> Optional[Path]:
141
+ """The repo root of the source checkout *start* sits in, if any."""
142
+ tree = source_tree_package_dir(start)
143
+ return None if tree is None else tree.parent.parent
144
+
145
+
146
+ def head_tree_hash(repo_root: Path) -> Optional[str]:
147
+ """`git rev-parse HEAD^{tree}` for *repo_root*, or None if unavailable.
148
+
149
+ None (rather than raising) covers every reason a tree hash might not be
150
+ resolvable -- no git binary, not a git repo, no commits yet -- and each
151
+ one means the same thing to a caller: pinning cannot proceed, run
152
+ unpinned. `never-touch.md`'s "stop at a boundary" posture, not a bug to
153
+ chase down.
154
+ """
155
+ try:
156
+ result = subprocess.run(
157
+ ["git", "-C", str(repo_root), "rev-parse", "HEAD^{tree}"],
158
+ capture_output=True, text=True, check=True,
159
+ )
160
+ except (subprocess.CalledProcessError, OSError):
161
+ return None
162
+ tree_hash = result.stdout.strip()
163
+ return tree_hash or None
164
+
165
+
166
+ def pin_cache_root() -> Path:
167
+ """Where materialized pins live -- always outside any working tree.
168
+
169
+ A platform cache directory keyed by tree hash, per the work unit's
170
+ flag-scope table: putting it inside the tree it names would make its own
171
+ key (`HEAD^{tree}`) move every time the pin is written. `$TMPDIR` (or its
172
+ override) rather than a dotfile under the repo for the same reason.
173
+ """
174
+ override = os.environ.get(_PIN_CACHE_ENV_VAR)
175
+ if override:
176
+ return Path(override)
177
+ return Path(tempfile.gettempdir()) / "specfuse-pins"
178
+
179
+
180
+ def pin_dir_for(tree_hash: str) -> Path:
181
+ return pin_cache_root() / tree_hash
182
+
183
+
184
+ def _pin_manifest_lines(pin_specfuse_dir: Path) -> list:
185
+ """Relative paths of every file under *pin_specfuse_dir*, sorted."""
186
+ return sorted(
187
+ str(p.relative_to(pin_specfuse_dir))
188
+ for p in pin_specfuse_dir.rglob("*") if p.is_file()
189
+ )
190
+
191
+
192
+ def _pin_is_complete(pin_dir: Path, tree_hash: str) -> bool:
193
+ """Whether *pin_dir* is a matching, fully-present pin for *tree_hash*.
194
+
195
+ Checks the marker's content (as before) AND that every file the manifest
196
+ recorded at materialize time is still present -- a manifest recorded once
197
+ and never re-derived from the possibly-reaped directory, so a pin missing
198
+ files fails this check instead of being reported as complete because most
199
+ of it happens to still be there.
200
+ """
201
+ marker = pin_dir / _PIN_MARKER_NAME
202
+ if not (marker.is_file()
203
+ and marker.read_text(encoding="utf-8").strip() == tree_hash):
204
+ return False
205
+ manifest = pin_dir / _PIN_MANIFEST_NAME
206
+ if not manifest.is_file():
207
+ return False
208
+ try:
209
+ expected = [line for line in
210
+ manifest.read_text(encoding="utf-8").splitlines() if line]
211
+ except OSError:
212
+ return False
213
+ specfuse_dir = pin_dir / "specfuse"
214
+ return bool(expected) and all(
215
+ (specfuse_dir / rel).is_file() for rel in expected)
216
+
217
+
218
+ def materialize_pin(repo_root: Path, tree_hash: str) -> Path:
219
+ """Copy *repo_root*'s `specfuse/` package to a pin keyed on *tree_hash*.
220
+
221
+ Idempotent: a pin already materialized for this tree hash -- marker
222
+ matches AND every manifested file is still present, `_pin_is_complete`
223
+ -- is reused as-is, so a second process pinning the same commit does not
224
+ re-copy. A pin that is missing files (an age-based tmp reaper, or
225
+ anything else, deleting from under an intact marker) fails that check
226
+ and is rebuilt from scratch here rather than reused half-complete
227
+ (FEAT-2026-0109/T09). Builds into a sibling temp directory first and
228
+ installs it with a single `os.replace` so a reader never observes a
229
+ half-copied pin.
230
+ """
231
+ pin_dir = pin_dir_for(tree_hash)
232
+ if _pin_is_complete(pin_dir, tree_hash):
233
+ return pin_dir
234
+
235
+ cache_root = pin_cache_root()
236
+ cache_root.mkdir(parents=True, exist_ok=True)
237
+ staging = Path(tempfile.mkdtemp(prefix=f".{tree_hash}-", dir=str(cache_root)))
238
+ try:
239
+ shutil.copytree(
240
+ repo_root / "specfuse", staging / "specfuse",
241
+ ignore=shutil.ignore_patterns("__pycache__", "*.pyc"),
242
+ )
243
+ (staging / PIN_LAUNCHER_NAME).write_text(_PIN_LAUNCHER_SOURCE, encoding="utf-8")
244
+ (staging / _PIN_MARKER_NAME).write_text(tree_hash, encoding="utf-8")
245
+ manifest = _pin_manifest_lines(staging / "specfuse")
246
+ (staging / _PIN_MANIFEST_NAME).write_text(
247
+ "\n".join(manifest) + ("\n" if manifest else ""), encoding="utf-8")
248
+ if pin_dir.exists():
249
+ if _pin_is_complete(pin_dir, tree_hash):
250
+ # Another process won the race and finished first; its copy
251
+ # is equally valid (same tree hash, fully present), so keep
252
+ # it and discard ours.
253
+ shutil.rmtree(staging, ignore_errors=True)
254
+ else:
255
+ # Stale marker, or a prior pin that lost files -- replace it.
256
+ shutil.rmtree(pin_dir, ignore_errors=True)
257
+ os.replace(staging, pin_dir)
258
+ else:
259
+ os.replace(staging, pin_dir)
260
+ finally:
261
+ if staging.exists():
262
+ shutil.rmtree(staging, ignore_errors=True)
263
+ return pin_dir
264
+
265
+
266
+ def pinned_build_info(running: Path) -> "Optional[tuple[str, Path]]":
267
+ """If *running* is a recorded pin, return `(tree_hash, pin_root)`; else None.
268
+
269
+ Trusts only a marker directly under a pin whose directory name equals the
270
+ marker's own content -- an out-of-tree build that merely happens to sit
271
+ under the cache root without a matching marker is still unidentified, not
272
+ a free pass.
273
+ """
274
+ pin_root = running.parent.parent # running == pin_root/specfuse/loop
275
+ marker = pin_root / _PIN_MARKER_NAME
276
+ if not marker.is_file():
277
+ return None
278
+ try:
279
+ recorded = marker.read_text(encoding="utf-8").strip()
280
+ except OSError:
281
+ return None
282
+ if not recorded or pin_root.name != recorded:
283
+ return None
284
+ return recorded, pin_root
285
+
286
+
287
+ def out_of_tree_warning(start: Optional[Path] = None) -> Optional[str]:
288
+ """Return the warning text, or `None` when there is nothing to warn about.
289
+
290
+ Separated from the printing so the message can be asserted on directly.
291
+
292
+ Three states (FEAT-2026-0109/T08): in-tree (`None`), a **recorded pin**
293
+ (reports both tree hashes, no alarming language -- this driver
294
+ materialized it, from a named commit, this run), and an **unidentified**
295
+ out-of-tree build (the original, unchanged "confidently wrong" text --
296
+ still exactly as dangerous as the stale wheel that motivated this module).
297
+ """
298
+ tree = source_tree_package_dir(start)
299
+ if tree is None:
300
+ return None
301
+ running = running_package_dir()
302
+ if running == tree:
303
+ return None
304
+ pin_info = pinned_build_info(running)
305
+ if pin_info is not None:
306
+ pinned_hash, pin_root = pin_info
307
+ working_root = tree.parent.parent
308
+ working_hash = head_tree_hash(working_root)
309
+ return (
310
+ f"running a recorded pin of specfuse from {pin_root} "
311
+ f"(tree {pinned_hash}); the working tree at {working_root} is "
312
+ f"now at tree {working_hash or 'unknown'}."
313
+ )
314
+ return (
315
+ f"warning: running specfuse from {running}, but the working tree at "
316
+ f"{tree.parent.parent} carries its own source.\n"
317
+ f" This command is measuring the INSTALLED build, not your "
318
+ f"checkout — results can be confidently wrong rather than failing.\n"
319
+ f" Run instead: {_SHIM_HINT}"
320
+ )
321
+
322
+
323
+ def warn_if_out_of_tree(start: Optional[Path] = None, stream=None) -> Optional[str]:
324
+ """Print the warning to stderr when one applies; return what was printed.
325
+
326
+ stderr, not stdout: several of these commands have parseable stdout that a
327
+ caller consumes, and a diagnostic must not land in it.
328
+ """
329
+ message = out_of_tree_warning(start)
330
+ if message is not None:
331
+ print(message, file=stream if stream is not None else sys.stderr)
332
+ return message
@@ -53,6 +53,7 @@ RETROSPECTIVE_FILENAME = "RETROSPECTIVE.md"
53
53
  #: creates the artifact and the requirement that a `not_met` close carry it.
54
54
  FOLLOW_UPS_FILENAME = "FOLLOW-UPS.md"
55
55
  FOLLOW_UP_ENTRY_RE = re.compile(r"^### ", re.MULTILINE)
56
+ FOLLOW_UP_ENTRY_END_RE = re.compile(r"^#{1,3}\s", re.MULTILINE)
56
57
 
57
58
  #: `## Post-merge checklist` — the optional PLAN.md section a `met` close
58
59
  #: files as one `specfuse:post-merge` issue (FEAT-2026-0085/T03).
@@ -74,8 +75,12 @@ def parse_followup_entries(text: str) -> list[str]:
74
75
  """
75
76
  matches = list(FOLLOW_UP_ENTRY_RE.finditer(text))
76
77
  entries = []
77
- for i, m in enumerate(matches):
78
- end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
78
+ for m in matches:
79
+ # An entry ends at the next heading of any level up to `###`, not
80
+ # only the next `### `: a closing `## Discharged` section used to be
81
+ # filed as the tail of the last entry's issue body (#1729).
82
+ nxt = FOLLOW_UP_ENTRY_END_RE.search(text, m.end())
83
+ end = nxt.start() if nxt else len(text)
79
84
  entries.append(text[m.start():end].strip("\n") + "\n")
80
85
  return entries
81
86
 
@@ -140,6 +145,16 @@ def gate_section_heading_re(gate_n: int) -> re.Pattern:
140
145
  )
141
146
 
142
147
 
148
+ #: The feature-oracle verdict line a close's `## Measurements` section must
149
+ #: carry when the gate declares `feature_oracle` (FEAT-2026-0101/T02). The
150
+ #: gate's `feature_oracle` *is* the feature-level re-run
151
+ #: `close-discipline.md` §1 requires — see
152
+ #: [FEAT-2026-0057/G1-CLOSE/feature-oracle-is-a-different-question].
153
+ FEATURE_ORACLE_KEY = "feature_oracle"
154
+ FEATURE_ORACLE_VERDICT_RE = re.compile(
155
+ rf"^###\s+{re.escape(FEATURE_ORACLE_KEY)}:\s*(PASS|FAIL)\s*$", re.MULTILINE,
156
+ )
157
+
143
158
  GATE_REVIEW_FILENAME_TEMPLATE = "GATE-{next_gate:02d}-REVIEW.md"
144
159
 
145
160
 
@@ -339,6 +354,19 @@ CLOSING_REQUIREMENTS: dict[str, list[Requirement]] = {
339
354
  applies_when="criteria_artifact_present",
340
355
  enforced_by="check_criteria_state_well_formed",
341
356
  ),
357
+ Requirement(
358
+ id="close-n", wu_type="close", phase="pre-squash",
359
+ description=(
360
+ f"When the gate's GATE-NN.md declares {FEATURE_ORACLE_KEY}, "
361
+ "RETROSPECTIVE.md's Measurements section records a "
362
+ "PASS/FAIL verdict line naming it — the gate's "
363
+ f"{FEATURE_ORACLE_KEY} is the feature-level re-run "
364
+ "close-discipline.md §1 requires "
365
+ "[FEAT-2026-0057/G1-CLOSE/feature-oracle-is-a-different-question]"
366
+ ),
367
+ file=RETROSPECTIVE_FILENAME,
368
+ enforced_by="check_feature_oracle_verdict_recorded",
369
+ ),
342
370
  ],
343
371
  "close-intermediate": [
344
372
  Requirement(
@@ -0,0 +1 @@
1
+ 0.18.0
@@ -86,6 +86,16 @@ it mostly does not need. Author-set unless marked driver-owned.
86
86
  - `generated_surfaces` — OPTIONAL. Generated files this unit's acceptance depends on.
87
87
  - `oracle_env` — OPTIONAL. Where the verifying oracle runs: `macos_local`,
88
88
  `linux_docker`, `github_actions_ci`, or an operator-named string.
89
+ - `feature_oracle` — GATE-level, not WU-level: lives in `GATE-NN.md`
90
+ frontmatter, one per gate. The executable proof of that gate's definition of
91
+ done, exercising the user-visible outcome end to end. Required to start
92
+ **red** and go **green** at the gate's walking skeleton (its first
93
+ implementation unit, permitted to stub anything off the thinnest end-to-end
94
+ path — `.specfuse/skills/authoring-work-units/SKILL.md` §14). The driver
95
+ runs it as part of every unit's verification in that gate and again at the
96
+ close, which records the verdict. `GATE.template.md` carries the key;
97
+ `/draft-feature` refuses to draft a gate without one
98
+ (`.specfuse/rules/close-discipline.md` §5, FEAT-2026-0101).
89
99
  - `produces` — OPTIONAL. Path(s) or glob(s) this unit must yield; the driver's
90
100
  presence gate refuses `complete` when one is missing or empty.
91
101
  - `produces_driver_helper` — OPTIONAL. Symbol(s) this unit adds to the driver.
@@ -295,11 +305,16 @@ line.
295
305
 
296
306
  **Unfinished work becomes tracked issues, not prose.** After a `not_met`
297
307
  close's squash, `file_followup_issues` files one GitHub issue per
298
- `FOLLOW-UPS.md` entry under `specfuse:follow-up`, carrying the entry body
299
- verbatim and idempotent per entry; on a `met` close it files the optional
300
- `## Post-merge checklist` section as one `specfuse:post-merge` issue. `gh`
301
- absent or failing leaves `FOLLOW-UPS.md` itself as the record — the driver
302
- never deletes or rewrites it — and emits one `followups_recorded` event naming
308
+ `FOLLOW-UPS.md` entry under `specfuse:follow-up`, titled from the entry's
309
+ heading text (`[FEAT-2026-0155 follow-up] <heading>`), carrying the entry body
310
+ verbatim behind a `<!-- specfuse:followup id=… -->` marker, and writing
311
+ `**Tracked as #N.**` back under the heading so the entry is filed once across
312
+ every later attempt — an entry that already carries that line, by the driver's
313
+ hand or a person's, is skipped; on a `met` close it files the optional
314
+ `## Post-merge checklist` section as one `specfuse:post-merge` issue and writes
315
+ the same line under that heading in `PLAN.md`. `gh` absent or failing leaves
316
+ `FOLLOW-UPS.md` itself as the record — the driver never deletes or rewrites an
317
+ entry, it only adds the tracked line — and emits one `followups_recorded` event naming
303
318
  the `filed` and `unfiled` counts.
304
319
 
305
320
  **Re-firing the flips out of band** (FEAT-2026-0070) — `fire_terminal_flips`
@@ -359,7 +374,7 @@ bound to the emitter by `tests/test_attempt_outcome_contract.py`:
359
374
  | `closing_deliverable_missing` | a closing-WU guard refused (see `close-discipline.md` §4) |
360
375
  | `deliverable_missing` | a declared `produces:` path is absent |
361
376
  | `no_deliverable_files` | the squash names only the WU file / events |
362
- | `produces_not_in_diff` | `produces:` path exists but is not in the squash |
377
+ | `produces_not_in_diff` | `produces:` path exists but is not in the squash, and the RESULT's `produces_unchanged:` did not justify it |
363
378
  | `squash_commit_failed` | `git commit` for the squash was rejected |
364
379
  | `smoke_import_failed` | a declared smoke-import line failed post-squash |
365
380
  | `learnings_not_staged` | a closing WU under `autonomy_default: auto` touched `.specfuse/LEARNINGS.md` directly instead of staging to `LEARNINGS-pending.md` (FEAT-2026-0053/T09) |
@@ -555,6 +570,15 @@ a roadmap-anchor check (with a loud flag if the goal itself seems to be drifting
555
570
  and open questions — each mapped to the draft WU it affects. The summary is
556
571
  advisory and owns no state.
557
572
 
573
+ **The oracle-progression obligation.** `plan-next` also drafts the next gate's
574
+ `feature_oracle`, and its review summary must state how the drafted oracle
575
+ advances the prior gate's oracle — in prose, as a judgment call, not as a
576
+ lint rule. Nothing can decide mechanically whether one shell command is a
577
+ "stronger" proof than another, so this is the one place an
578
+ unenforceable-but-important question belongs: the summary is already weighted
579
+ toward doubt and already carries the roadmap-anchor check above it
580
+ (FEAT-2026-0101).
581
+
558
582
  ## 8. LEARNINGS — the cross-feature feedback loop
559
583
 
560
584
  `LEARNINGS.md` is an append-only, cross-feature log of durable, reusable rules
@@ -24,10 +24,32 @@ WU's frontmatter so the auto-close predicate cannot optimize it away (#189).
24
24
  The close re-runs every oracle the feature's acceptance criteria name — the
25
25
  full test command(s), plus any compile/execution gates — fresh, in this
26
26
  session, exit codes read directly. Never inherit a producing WU's
27
- self-report: `done` is a claim, the re-run is the verification. When the
27
+ self-report: `done` is a claim, the re-run is the verification. What may be
28
+ inherited is narrower and stated in §5: on a **re-close** of the same gate, a
29
+ `narrow` criterion's green recorded in `GATE-NN-CRITERIA.md` by an earlier
30
+ close attempt is carried forward; every `broad` oracle and the gate's
31
+ `feature_oracle` re-run on every attempt. "Fresh" binds the first close of a
32
+ gate in full, and the broad half of every close after it — never a producing
33
+ unit's own report. When the
28
34
  feature's criteria assert on generated artifacts, regenerate into a clean
29
35
  output directory before asserting — stale output satisfies any assertion.
30
36
 
37
+ **The gate's `feature_oracle` is the feature-level re-run, not one more unit
38
+ oracle.** Re-running every producing unit's own oracle is a different, weaker
39
+ question: `[FEAT-2026-0057/G1-CLOSE/feature-oracle-is-a-different-question]`
40
+ records a close where fifteen unit oracles re-ran green and none could
41
+ observe the defect, because none asserted on the path the feature was meant
42
+ to change. A gate's `GATE-NN.md` may declare one `feature_oracle` — the
43
+ composite question the units were each too small to ask. When it does, the
44
+ close re-runs it fresh, same as any other oracle above, and records its
45
+ verdict as a `### feature_oracle: PASS` or `### feature_oracle: FAIL` line in
46
+ `## Measurements`. `specfuse lint --closing` fails a close that omits this
47
+ line when the gate declares an oracle (`close-n`,
48
+ `check_feature_oracle_verdict_recorded` in `closing_requirements.py` /
49
+ `lint_closing.py`); a gate that declares no `feature_oracle` imposes no such
50
+ requirement here — see this rule's "What the driver checks" section and T03
51
+ for whether an absent declaration is itself a finding.
52
+
31
53
  > **Provenance.** A WU reported `done` while its source was untouched and its
32
54
  > oracle never ran; the driver-side produces-vs-diff guard now refuses that
33
55
  > pass (specfuse-loop >= 0.3.21), but only the close's own fresh re-run
@@ -6,7 +6,7 @@ Licensed under the Apache License, Version 2.0. See LICENSE.
6
6
  # Rule: operator escalation framing
7
7
 
8
8
  Binding whenever work **stops and needs a human decision** — a `blocked` work
9
- unit, a gate at `awaiting_review`, a hedged verdict, a scope question, a refusal,
9
+ unit, a gate at `awaiting_review`, a `not_met` verdict, a scope question, a refusal,
10
10
  or any skill that asks before writing.
11
11
 
12
12
  The audience is someone **deciding**, not someone executing. They may not have
@@ -60,7 +60,7 @@ so in that line rather than dropping it silently.
60
60
  ## The feature briefing — required when the halt is at feature scope
61
61
 
62
62
  The six parts frame a **decision**. When the halt is at feature or gate scope —
63
- a hedged terminal verdict, a feature that stopped partway and needs a human —
63
+ a `not_met` terminal verdict, a feature that stopped partway and needs a human —
64
64
  the decision is not the whole question. The operator also needs to know whether
65
65
  the work *achieved what it was funded to achieve*, and that is a separate
66
66
  question from whether the tasks passed.
@@ -124,11 +124,11 @@ success is the failure this section prevents.
124
124
 
125
125
  - **Options without a recommendation.** Surfacing choices and stopping pushes the
126
126
  analysis back onto the person who asked. Always make the call; they can override.
127
- - **A refusal reported as a malfunction.** A withheld terminal flip on a hedged
127
+ - **A refusal reported as a malfunction.** A withheld terminal flip on a `not_met`
128
128
  verdict is the contract working. Reporting it in the same tone as a crash makes
129
129
  a correct system look broken and invites someone to "fix" the safety catch.
130
130
  - **Writing the human's own justification for them.** Where a field records *why a
131
- human accepted something* — `/accept-hedged-close`'s reason line, `/unblock-wu`'s
131
+ human decided something* — a `type: human` unit's `evidence:` line, `/unblock-wu`'s
132
132
  rationale, a contract-change acknowledgment — that text must come from the human.
133
133
  An agent that drafts it has removed the signature it was collecting. Ask; do not
134
134
  supply.
@@ -35,14 +35,19 @@ cycle; this file is normative on how the loop surface reports step 4.
35
35
  the work unit's **Do not touch** section is binding.
36
36
  3. **Verify.** Re-read what you produced — the Write/Edit tool reports the
37
37
  action it took, not the property you wanted — and run the work unit's own
38
- verification commands, in declared order, with full output. "I assume the
38
+ verification commands, in declared order, with full output. On the loop
39
+ that set is the **per-attempt (narrow) tier**: the unit type's gates minus
40
+ any declaring `tier: broad`, with `tests` through its `narrow_command` over
41
+ the unit's `produces:` test modules. The full suite, coverage and every
42
+ `tier: broad` gate are the driver's, once per gate — running them in-session
43
+ buys nothing the driver does not re-run. "I assume the
39
44
  tests still pass" is not a verification. A behavioural claim needs a run, not
40
45
  a reading of the source; a rule-or-severity claim needs a **negative
41
46
  observation**, the rule seen rejecting a purpose-built bad input.
42
47
  4. **Report.** Report only what verification confirmed.
43
48
 
44
49
  A failing check leaves you in one of three situations: correctable locally (fix
45
- the cause, re-run the **full** gate set from the top), spinning (three fresh
50
+ the cause, re-run the **whole narrow tier** from the top), spinning (three fresh
46
51
  attempts is the driver's budget — emit `status: blocked` with the evidence
47
52
  rather than guessing), or fundamentally blocked (a spec ambiguity, generated
48
53
  code that must change, a missing dependency — emit `status: blocked` naming the
@@ -61,6 +66,9 @@ acceptance_criteria:
61
66
  met: true | false
62
67
  evidence: <how you know — a test name, a behavior, a line reference>
63
68
  blocked_reason: <present only when status is blocked>
69
+ produces_unchanged: # optional — closing obligation 1 below
70
+ - path: <a produces: entry, verbatim>
71
+ justification: <the command you ran and its output showing the deliverable already holds>
64
72
  ```
65
73
  ````
66
74
 
@@ -96,9 +104,12 @@ blocked_reason: <present only when status is blocked>
96
104
 
97
105
  1. **Diff against `produces:` first.** Every path in the WU's `produces:` list
98
106
  must show a working-tree change, or the RESULT must justify each unchanged
99
- path with the command and output showing the deliverable already holds.
100
- Silence on an unchanged deliverable is not a valid close (#198, outcome
101
- `produces_not_in_diff`).
107
+ path under `produces_unchanged:` — the entry spelled as `produces:` spells
108
+ it, plus the command and output showing the deliverable already holds. The
109
+ driver reads that list: a justified entry passes and is recorded on the
110
+ attempt as `produces_justified`; an unjustified one, or a blank
111
+ justification, is refused (#198, #3268, outcome `produces_not_in_diff`).
112
+ Silence on an unchanged deliverable is not a valid close.
102
113
  2. **A plan-level contradiction is `blocked`, not `complete`.** If the plan
103
114
  cannot be delivered as written, put the finding in `blocked_reason`; never
104
115
  write it into a gate document and close `complete`.
@@ -38,7 +38,10 @@ Two generic meanings, both apply unless the unit narrows the scope:
38
38
  - **Run the declared verification commands.** The unit names the checks that
39
39
  decide done. Run them yourself first, in declared order, with full output.
40
40
  Generic verifications — "I assume the tests still pass" — are not verifications.
41
- The commands are the verification.
41
+ The commands are the verification. Where a surface tiers its checks — the
42
+ loop's per-attempt narrow tier against the driver's once-per-gate broad run —
43
+ the declared set is the tier the surface assigns to the session; running the
44
+ broader tier in-session proves nothing the surface does not re-run itself.
42
45
  - **Observe behavior, not source.** A claim that code does X is verified by making
43
46
  X happen and reading the output — never by reading the source that looks like it
44
47
  does X. A rule read as `ERROR` in source may be gated off and never fire; only a
@@ -50,8 +53,8 @@ Two generic meanings, both apply unless the unit narrows the scope:
50
53
  decided by the checks passing, not by the agent's belief. If a check fails you are
51
54
  in one of three situations:
52
55
 
53
- 1. **Correctable locally.** Fix the cause, re-run the **full** check set from the
54
- top, then continue. A re-run that fixes the original failure but introduces a
56
+ 1. **Correctable locally.** Fix the cause, re-run the **whole declared** check set
57
+ from the top, then continue. A re-run that fixes the original failure but introduces a
55
58
  new one is still a failed cycle.
56
59
  2. **Spinning threshold reached** (typically three fresh attempts on one unit).
57
60
  Signal blocked with precise evidence rather than spending the cycle guessing.