specfuse-loop 0.3.7__tar.gz → 0.3.9__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 (143) hide show
  1. {specfuse_loop-0.3.7/specfuse_loop.egg-info → specfuse_loop-0.3.9}/PKG-INFO +1 -1
  2. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/pyproject.toml +1 -1
  3. specfuse_loop-0.3.9/specfuse/loop/data/VERSION +1 -0
  4. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/rules/correlation-ids.md +6 -6
  5. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/rules/never-touch.md +10 -8
  6. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/rules/result-contract.md +7 -0
  7. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/rules/security-boundaries.md +9 -10
  8. specfuse_loop-0.3.9/specfuse/loop/data/rules/verification-discipline.md +81 -0
  9. specfuse_loop-0.3.9/specfuse/loop/data/schemas/event.schema.json +78 -0
  10. specfuse_loop-0.3.9/specfuse/loop/data/schemas/events/initiative_created.schema.json +42 -0
  11. specfuse_loop-0.3.9/specfuse/loop/data/schemas/events/spec_issue_resolved.schema.json +36 -0
  12. specfuse_loop-0.3.9/specfuse/loop/data/schemas/events/spec_issue_routed.schema.json +31 -0
  13. specfuse_loop-0.3.9/specfuse/loop/data/schemas/events/spec_validated.schema.json +77 -0
  14. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/loop.py +20 -4
  15. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/scaffold.py +2 -1
  16. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/validate_event.py +28 -30
  17. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9/specfuse_loop.egg-info}/PKG-INFO +1 -1
  18. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse_loop.egg-info/SOURCES.txt +7 -0
  19. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_init_integration.py +7 -0
  20. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_post_pass_invariant.py +24 -0
  21. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_prepare_feature.py +38 -0
  22. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_roadmap_add_skill.py +93 -5
  23. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_data_in_sync.py +6 -0
  24. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_init.py +12 -0
  25. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_manifest.py +1 -1
  26. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_resources.py +6 -0
  27. specfuse_loop-0.3.9/tests/test_skills_vendored_in_sync.py +70 -0
  28. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_validate_event.py +52 -51
  29. specfuse_loop-0.3.7/specfuse/loop/data/VERSION +0 -1
  30. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/LICENSE +0 -0
  31. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/NOTICE +0 -0
  32. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/README.md +0 -0
  33. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/setup.cfg +0 -0
  34. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/__init__.py +0 -0
  35. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/_miniyaml.py +0 -0
  36. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/adopt_feature.py +0 -0
  37. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/LEARNINGS.template.md +0 -0
  38. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +0 -0
  39. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/docs/concepts/ralph-lineage.md +0 -0
  40. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/docs/getting-started.md +0 -0
  41. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/docs/methodology.md +0 -0
  42. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/docs/skills.md +0 -0
  43. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/gitignore.snippet +0 -0
  44. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/roadmap.template.md +0 -0
  45. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/templates/GATE.template.md +0 -0
  46. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/templates/PLAN.template.md +0 -0
  47. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/templates/WU.template.md +0 -0
  48. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/data/verification.yml.example +0 -0
  49. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/gate_eval.py +0 -0
  50. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/gh_backend.py +0 -0
  51. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/gh_features.py +0 -0
  52. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse/loop/lint_plan.py +0 -0
  53. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  54. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse_loop.egg-info/entry_points.txt +0 -0
  55. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse_loop.egg-info/requires.txt +0 -0
  56. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/specfuse_loop.egg-info/top_level.txt +0 -0
  57. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_adopt_feature.py +0 -0
  58. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_arm_gate_edits_uncommitted.py +0 -0
  59. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_attempt_outcome_emission.py +0 -0
  60. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_autosync.py +0 -0
  61. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_autosync_consent.py +0 -0
  62. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_autosync_firstrun.py +0 -0
  63. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_autosync_plugin.py +0 -0
  64. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_backend.py +0 -0
  65. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_bookkeeping_commit_crash_run.py +0 -0
  66. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  67. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_closing_deliverable_guard.py +0 -0
  68. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_cost_tracking.py +0 -0
  69. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_deliverable_presence_gate.py +0 -0
  70. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_doctor.py +0 -0
  71. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_driver_integration.py +0 -0
  72. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_driver_lock.py +0 -0
  73. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_duration_tracking.py +0 -0
  74. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_empty_files_escalation.py +0 -0
  75. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_ensure_feature_branch.py +0 -0
  76. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_events_redaction.py +0 -0
  77. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_extra_gates.py +0 -0
  78. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_force_full_close.py +0 -0
  79. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_gate_eval.py +0 -0
  80. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_gate_eval_calibration.py +0 -0
  81. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  82. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_gate_eval_terminal_wiring.py +0 -0
  83. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_gh_backend.py +0 -0
  84. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_gh_features.py +0 -0
  85. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_git_env_isolation.py +0 -0
  86. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_hashed_denylist.py +0 -0
  87. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_hashed_denylist_ci.py +0 -0
  88. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_leak_findings_redaction.py +0 -0
  89. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_leak_scan.py +0 -0
  90. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_leak_scan_content.py +0 -0
  91. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_learnings_query.py +0 -0
  92. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  93. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lifecycle_integration.py +0 -0
  94. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_bare_produces_path.py +0 -0
  95. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_close_intermediate.py +0 -0
  96. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_close_wu.py +0 -0
  97. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_correlation_id.py +0 -0
  98. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  99. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_oracle_env.py +0 -0
  100. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_plan_errors.py +0 -0
  101. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_plan_next_draft.py +0 -0
  102. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_plan_relative_import.py +0 -0
  103. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_produces_driver_helper.py +0 -0
  104. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_sections.py +0 -0
  105. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  106. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_auto_archive.py +0 -0
  107. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_caveman_preamble.py +0 -0
  108. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_close_intermediate.py +0 -0
  109. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_defaults_by_type.py +0 -0
  110. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_effort.py +0 -0
  111. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_failure_note_cap.py +0 -0
  112. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_files_changed_guard.py +0 -0
  113. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_gate_budget.py +0 -0
  114. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_model_alias.py +0 -0
  115. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_orchestration.py +0 -0
  116. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_reset_preserving_events.py +0 -0
  117. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_smoke_runner.py +0 -0
  118. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_unsandboxed.py +0 -0
  119. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_loop_zero_token_guard.py +0 -0
  120. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_migrate_legacy.py +0 -0
  121. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_miniyaml_equivalence.py +0 -0
  122. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_miniyaml_negative.py +0 -0
  123. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_planned_cost_lint.py +0 -0
  124. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_produces_field.py +0 -0
  125. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_result_block.py +0 -0
  126. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_roadmap_archive_skill.py +0 -0
  127. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_roadmap_row_parser.py +0 -0
  128. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_docs.py +0 -0
  129. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_seed_sanity.py +0 -0
  130. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_upgrade.py +0 -0
  131. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_scaffold_wiring.py +0 -0
  132. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_squash_commit_hook_crash.py +0 -0
  133. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_template_closing_shapes.py +0 -0
  134. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_terminal_flip_ownership.py +0 -0
  135. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_terminal_flips.py +0 -0
  136. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_untracked_feature_folder.py +0 -0
  137. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_upgrade_integration.py +0 -0
  138. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_upgrade_merge_gate.py +0 -0
  139. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_verdict_coupling.py +0 -0
  140. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_verify_empty_gate_set.py +0 -0
  141. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_version_consistency.py +0 -0
  142. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_version_skew.py +0 -0
  143. {specfuse_loop-0.3.7 → specfuse_loop-0.3.9}/tests/test_wu_execution_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.3.7
3
+ Version: 0.3.9
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.3.7"
7
+ version = "0.3.9"
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"
@@ -0,0 +1 @@
1
+ 0.3.9
@@ -10,9 +10,9 @@ feature folder, the WU file, the event log, the branch, and the commit history.
10
10
  This rule defines the format, the surfaces the ID must appear on, how the next ID
11
11
  is chosen, and what to do when you see — or are about to produce — a malformed one.
12
12
 
13
- [`docs/methodology.md`](../../docs/methodology.md) §1 is normative on the
14
- correlation-ID scheme; this file is the operational reference every session reads
15
- before emitting an ID.
13
+ `methodology.md` §1 (the gate-cycle canonical, in this shared substrate set) is
14
+ normative on the correlation-ID scheme; this file is the operational reference
15
+ every session reads before emitting an ID.
16
16
 
17
17
  ## Format
18
18
 
@@ -192,6 +192,6 @@ any downstream tooling filter and join on these IDs; a typo breaks the thread.
192
192
  feature crosses a calendar boundary.
193
193
 
194
194
  When you detect a malformed ID in your own work in progress, fix it before reporting.
195
- When you detect one in an artifact that is already committed, emit
196
- `status: blocked` rather than silently rewriting history — the
197
- [result-contract](result-contract.md) discipline applies here too.
195
+ When you detect one in an artifact that is already committed, signal blocked
196
+ rather than silently rewriting history — the
197
+ [verification-discipline](verification-discipline.md) applies here too.
@@ -8,9 +8,10 @@ Licensed under the Apache License, Version 2.0. See LICENSE.
8
8
  Three categories of path are off-limits to every work-unit session, regardless of
9
9
  unit type. If a unit's acceptance criteria or verification commands appear to
10
10
  require modifying something in this list, that is an escalation condition, not a
11
- license to proceed — emit `status: blocked` per
12
- [`result-contract.md`](result-contract.md) with a `blocked_reason` naming the
13
- boundary.
11
+ license to proceed — signal blocked per
12
+ [`verification-discipline.md`](verification-discipline.md), naming the
13
+ boundary. (How "blocked" is signalled is surface-specific — a `status: blocked`
14
+ RESULT on the loop, a `blocked_*` transition on the orchestrator.)
14
15
 
15
16
  ## 1. Generated code directories
16
17
 
@@ -29,7 +30,7 @@ leaves no trace of why.
29
30
 
30
31
  When a generated file is wrong, the response is to change the spec or the generator
31
32
  that produced it, not the file itself. If that is outside this unit's boundary,
32
- emit `status: blocked` with the spec/generator change named.
33
+ signal blocked with the spec/generator change named.
33
34
 
34
35
  ## 2. Secrets and credentials
35
36
 
@@ -53,9 +54,10 @@ You must not:
53
54
 
54
55
  ## 3. `.git/` internals
55
56
 
56
- The `.git/` directory is the repository's internal state. The driver owns all git
57
- operations for work units (see [`result-contract.md`](result-contract.md) rule 1);
58
- you do not run `git` at all. Beyond that, never write under `.git/` directly:
57
+ The `.git/` directory is the repository's internal state. On the loop surface the
58
+ driver owns all git operations for work units and the session does not run `git`
59
+ at all; on the orchestrator surface an agent may use `git`/`gh` within its unit's
60
+ scope. Either way, never write under `.git/` directly:
59
61
 
60
62
  - No edits to `.git/hooks/*` to bypass checks. If a hook is failing, the right
61
63
  response is to diagnose the underlying issue.
@@ -77,5 +79,5 @@ response to a failing gate is to fix what it is flagging, not to silence it.
77
79
  Before writing to any path, confirm it is not in one of the three categories above.
78
80
  If you are uncertain whether a path is "generated" or "hand-written," the repo's
79
81
  declaration is authoritative; if the declaration is missing or ambiguous, treat
80
- the path as generated and emit `status: blocked` rather than write. Silence at a
82
+ the path as generated and signal blocked rather than write. Silence at a
81
83
  boundary is not permission.
@@ -18,6 +18,13 @@ wastes an attempt and teaches the next session nothing.
18
18
 
19
19
  ## The four-step discipline
20
20
 
21
+ This contract is the loop-surface expression of the neutral
22
+ [`verification-discipline.md`](verification-discipline.md) rule (vendored from the
23
+ Specfuse methodology core): it extends that rule's **state intent → act → verify →
24
+ report** cycle with the loop's concrete RESULT-block shape and status vocabulary.
25
+ The neutral rule is normative on *what* the discipline requires; this file is
26
+ normative on *how* the loop surface reports it.
27
+
21
28
  Every session operates on the same cycle: **state intent, act, verify, report.** The
22
29
  RESULT block is step 4, but it is only trustworthy when steps 1–3 ran first.
23
30
 
@@ -25,8 +25,7 @@ anything conventionally treated as a credential (see the enumeration in
25
25
  This applies whether the value came from a real secret or was constructed.
26
26
  - **Never commit.** Secrets files are excluded from commits categorically. A
27
27
  `.env` in your working directory is a mistake; remove it before the driver
28
- commits, not after. If you discover a secret already committed, emit
29
- `status: blocked` immediately — do not attempt history rewriting.
28
+ commits, not after. If you discover a secret already committed, signal blocked immediately — do not attempt history rewriting.
30
29
  - **Reference by name, not by value.** When a command needs a secret, pass it via
31
30
  an environment variable reference (`$GITHUB_TOKEN`) rather than substituting
32
31
  the value into the command line. Do not expand secret variables into log output;
@@ -51,15 +50,15 @@ is almost always a unit-definition problem, not a license to break the rule.
51
50
  2. **Re-read the unit.** Verify you have understood the step correctly. Often the
52
51
  unit describes how the *human* will verify, with the session doing an
53
52
  upstream-only step.
54
- 3. **If the requirement is genuine, escalate via the RESULT block.** Emit
55
- `status: blocked` with a precise `blocked_reason` naming the privilege
56
- required (e.g., "verification command requires reading
57
- `config/db-prod.env`"). The driver halts the gate and the human decides
58
- whether to adjust the unit, run the step out-of-band, or provide a scoped
59
- credential.
53
+ 3. **If the requirement is genuine, signal blocked.** Name the privilege
54
+ required (e.g., "verification command requires reading `config/db-prod.env`")
55
+ as the blocked reason. Work halts and the human decides whether to adjust the
56
+ unit, run the step out-of-band, or provide a scoped credential. (The signal is
57
+ surface-specific a `status: blocked` RESULT on the loop, a `blocked_*`
58
+ transition on the orchestrator.)
60
59
  4. **Do not report the unit complete.** A verification that could not be run is
61
60
  not a verification; a unit whose verification cannot be run is not done (see
62
- [`result-contract.md`](result-contract.md)).
61
+ [`verification-discipline.md`](verification-discipline.md)).
63
62
 
64
63
  The common mistake here is to "helpfully" substitute a weaker check for a
65
64
  privileged one — "I couldn't run the secret-requiring command, so I inspected the
@@ -76,7 +75,7 @@ yours, and you are not expected to inspect or transmit them. The prohibition is
76
75
  unit's scope* — not on using a credentialed tool to do the work the unit names.
77
76
 
78
77
  If an authenticated tool returns an error that implies a credential problem
79
- (expired token, permission denied, 401/403), stop and emit `status: blocked`. Do
78
+ (expired token, permission denied, 401/403), stop and signal blocked. Do
80
79
  not attempt to re-authenticate, reconfigure the credential, or swap accounts.
81
80
 
82
81
  ## Log hygiene
@@ -0,0 +1,81 @@
1
+ <!--
2
+ Copyright 2026 Specfuse Contributors
3
+ Licensed under the Apache License, Version 2.0. See LICENSE.
4
+ -->
5
+
6
+ # Rule: the verification discipline
7
+
8
+ Every unit of work runs the same four-step cycle: **state intent, act, verify,
9
+ report.** This is the surface-neutral core of the discipline. Each execution
10
+ surface expresses step 4 (report) in its own shape — the loop's RESULT block, the
11
+ orchestrator's `task_completed` event — but the cycle, and the honesty it demands,
12
+ are identical everywhere. This rule is core substrate; the surface-specific report
13
+ shapes (`result-contract.md` on the loop, `verify-before-report.md` on the
14
+ orchestrator) build on it and must not weaken it.
15
+
16
+ ## The four steps
17
+
18
+ ### 1. State intent
19
+ Before acting, state in one sentence what you are about to do and why it is the
20
+ next right step. If you cannot state it clearly, you are not ready to act; if the
21
+ intent disagrees with the unit's acceptance criteria, the unit is not what you
22
+ thought it was. Stay factual and present-tense.
23
+
24
+ ### 2. Act
25
+ Perform the change within the scope declared in step 1. If the act expands beyond
26
+ the intent — "while I was here I also fixed X" — either revise the intent and
27
+ stay, or stop and leave X for another unit. Drift between intent and action is the
28
+ most common source of ambiguous diffs. The unit's **Do not touch** boundary is
29
+ binding.
30
+
31
+ ### 3. Verify
32
+ Two generic meanings, both apply unless the unit narrows the scope:
33
+
34
+ - **Re-read the produced artifact.** After writing a file, read it back; after
35
+ editing a fixture, inspect the result. The Write/Edit tool reports success for
36
+ the action it took, not for the property you wanted. This catches silent
37
+ truncation, encoding surprises, and "I thought I wrote X but wrote Y."
38
+ - **Run the declared verification commands.** The unit names the checks that
39
+ decide done. Run them yourself first, in declared order, with full output.
40
+ Generic verifications — "I assume the tests still pass" — are not verifications.
41
+ The commands are the verification.
42
+
43
+ **Verification is the exit oracle.** The agent's own report is advisory; done is
44
+ decided by the checks passing, not by the agent's belief. If a check fails you are
45
+ in one of three situations:
46
+
47
+ 1. **Correctable locally.** Fix the cause, re-run the **full** check set from the
48
+ top, then continue. A re-run that fixes the original failure but introduces a
49
+ new one is still a failed cycle.
50
+ 2. **Spinning threshold reached** (typically three fresh attempts on one unit).
51
+ Signal blocked with precise evidence rather than spending the cycle guessing.
52
+ 3. **Fundamentally blocked.** A spec ambiguity, generated code that must change, a
53
+ missing dependency — signal blocked immediately, naming the boundary.
54
+
55
+ ### 4. Report
56
+ Report only what verification confirmed. An optimistic report is worse than none:
57
+ the driver (or merge gate) re-runs the checks, discovers the gap, the attempt is
58
+ wasted, and the next session inherits no useful evidence about why. The concrete
59
+ report shape is surface-specific — see `result-contract.md` (loop RESULT block) or
60
+ `verify-before-report.md` (orchestrator event). Both carry the same obligation:
61
+ no claim of completion without the verification evidence behind it.
62
+
63
+ ## Blocked is a respectable outcome
64
+
65
+ Signalling blocked after one honest attempt is cheaper than three fresh attempts
66
+ chasing a completion the checks keep rejecting. When an escalation trigger fires,
67
+ or a unit's acceptance criteria appear to require crossing a boundary named in
68
+ `never-touch.md` or `security-boundaries.md`, **stop and signal blocked with a
69
+ precise reason naming the boundary** — do not work around it. Silence at a
70
+ boundary is not permission. How "blocked" is signalled is surface-specific (a
71
+ `status: blocked` RESULT on the loop; a `blocked_*` state transition plus
72
+ escalation on the orchestrator); the obligation to stop rather than improvise is
73
+ core.
74
+
75
+ ## Honesty over optimism
76
+
77
+ The whole coordination model runs on the report being an honest claim about what
78
+ happened. When a session skips verification, the cost is not its own — it is paid
79
+ by the next session that inherits a broken assumption, the driver that wastes
80
+ attempts, and the human who reviews evidence that turns out to be wrong. State
81
+ intent. Act. Verify. Report. Every time.
@@ -0,0 +1,78 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://specfuse.dev/orchestrator/schemas/event.schema.json",
4
+ "title": "Event log entry",
5
+ "$comment": "Additive history (WU 2.14, F2.4): Phase 1 baseline enum — feature_created, spec_validated, task_graph_drafted, plan_ready, plan_generated, plan_approved, task_created, task_ready, task_started, task_completed, task_blocked, spec_issue_raised, override_applied, override_expired, human_escalation. Phase 2 additions: template_coverage_checked (WU 2.6), feature_state_changed (WU 2.9). Phase 3 additions: test_plan_authored (WU 3.2), qa_execution_completed + qa_execution_failed (WU 3.3), qa_regression_filed + qa_regression_resolved + escalation_resolved (WU 3.4; escalation_resolved is substrate — resolution_kind discriminator, first application retires the Phase 2 orphan inbox file FEAT-2026-0005-plan-review-cycle.md; closes carry-item F2.10), regression_suite_curated (WU 3.5; emitted by the qa-curation skill on curation-PR merge; envelope correlation_id task-level on the qa_curation task; affected_feature_correlation_ids in payload carries the cross-feature fanout). Phase 4 additions: spec_issue_resolved + spec_issue_routed (WU 4.5; emitted by the specs agent's spec-issue-triage skill — spec_issue_resolved when a spec-content fix is applied in /product/, spec_issue_routed when the issue is filed against the generator project). Per-type payload schemas for these types live under shared/schemas/events/. NOTE: plan_reingested was considered in WU 2.14 but deliberately not added — WU 2.11's template_coverage_checked re-chain on every Phase B success already makes Phase B observable in the event log. Reframe migration (docs/naming-convention.md, additive): correlation_id pattern widened from ^FEAT-\\d{4}-\\d{4}(/T\\d{2})?$ to ^(FEAT|INIT)-\\d{4}-\\d{4}(/F\\d{2})?(/T\\d{2})?$ to carry the initiative/feature/work-unit hierarchy (INIT-YYYY-NNNN[/FNN][/TNN]) alongside the legacy/component-local FEAT- forms; existing FEAT- entries validate unchanged. Per-type payload schemas under events/ broadened their FEAT-only correlation/path patterns the same way.",
6
+ "description": "A single JSONL entry in a feature's event log. Event log semantics are defined in orchestrator-architecture.md §7.3.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "timestamp",
11
+ "correlation_id",
12
+ "event_type",
13
+ "source",
14
+ "source_version",
15
+ "payload"
16
+ ],
17
+ "properties": {
18
+ "timestamp": {
19
+ "type": "string",
20
+ "format": "date-time",
21
+ "description": "ISO 8601 timestamp with timezone offset (e.g. 2026-04-20T14:32:05Z or 2026-04-20T10:32:05-04:00).",
22
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$"
23
+ },
24
+ "correlation_id": {
25
+ "type": "string",
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/TNN; or the legacy/component-local feature FEAT-YYYY-NNNN and task FEAT-YYYY-NNNN/TNN. Existing FEAT- entries validate unchanged.",
27
+ "pattern": "^(FEAT|INIT)-\\d{4}-\\d{4}(/F\\d{2})?(/T\\d{2})?$"
28
+ },
29
+ "event_type": {
30
+ "type": "string",
31
+ "description": "Minimum event type set from architecture §7.3. New types must be registered in this schema before use.",
32
+ "enum": [
33
+ "initiative_created",
34
+ "feature_created",
35
+ "spec_validated",
36
+ "feature_graph_drafted",
37
+ "task_graph_drafted",
38
+ "template_coverage_checked",
39
+ "plan_ready",
40
+ "plan_generated",
41
+ "plan_approved",
42
+ "task_created",
43
+ "task_ready",
44
+ "task_started",
45
+ "task_completed",
46
+ "task_blocked",
47
+ "spec_issue_raised",
48
+ "override_applied",
49
+ "override_expired",
50
+ "human_escalation",
51
+ "feature_state_changed",
52
+ "test_plan_authored",
53
+ "qa_execution_completed",
54
+ "qa_execution_failed",
55
+ "qa_regression_filed",
56
+ "qa_regression_resolved",
57
+ "escalation_resolved",
58
+ "regression_suite_curated",
59
+ "spec_issue_resolved",
60
+ "spec_issue_routed"
61
+ ]
62
+ },
63
+ "source": {
64
+ "type": "string",
65
+ "description": "Emitter of the event: a fixed role (`specs`, `pm`, `qa`, `config-steward`, `merge-watcher`), the loop's single-repo `driver`, a component agent scoped by repo (`component:<name>`), or `human`. The `driver` value is the loop execution surface's sole emitter (single-repo gate-cycle); the orchestrator surface uses the role-scoped values. For `component:<name>`, `<name>` is the bare component repository name with no owner prefix — the short form of the git remote (e.g. `component:api-sample`, not `component:acme/api-sample`). This matches the `source: component:<repo>` convention used across roles; the PM and QA agents emit events with `source: pm` and `source: qa` respectively (no repo suffix, since those roles are not scoped by component repo).",
66
+ "pattern": "^(human|driver|specs|pm|qa|config-steward|merge-watcher|component:[a-z0-9][a-z0-9-]*)$"
67
+ },
68
+ "source_version": {
69
+ "type": "string",
70
+ "description": "Agent configuration version in effect when the event was emitted. Format is semver-like but not enforced here; `human` events use the orchestration repo commit SHA or `n/a`.",
71
+ "minLength": 1
72
+ },
73
+ "payload": {
74
+ "type": "object",
75
+ "description": "Event-specific structured content. Per-type payload schemas will be added under /shared/schemas/events/ as they stabilize."
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://specfuse.dev/orchestrator/schemas/events/initiative_created.schema.json",
4
+ "title": "initiative_created event payload",
5
+ "$comment": "Provenance: authored for the Model-B reframe (docs/naming-convention.md) as the initiative-level counterpart of feature_created. Emitted by the specs agent's initiative-intake skill (renamed from feature-intake) when a new INITIATIVE registry entry is created. Fields mirror feature_created with initiative_title in place of feature_title and an INIT-YYYY-NNNN correlation ID. feature_created is retained for legacy/feature-level use.",
6
+ "description": "Payload shape for the `initiative_created` event emitted by the specs agent during initiative intake. Captures the human-provided inputs (title, repos, autonomy) alongside the minted initiative correlation ID, duplicated from the envelope for payload self-containment.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "initiative_title",
11
+ "involved_repos",
12
+ "autonomy_default",
13
+ "correlation_id"
14
+ ],
15
+ "properties": {
16
+ "initiative_title": {
17
+ "type": "string",
18
+ "description": "Human-provided title for the initiative. Free-form prose; used in the initiative registry's Description header. Not a machine identifier — the correlation ID is the stable reference.",
19
+ "minLength": 1
20
+ },
21
+ "involved_repos": {
22
+ "type": "array",
23
+ "description": "Component repositories this initiative spans, as `owner/repo` strings. Mirrors the `involved_repos` frontmatter field at creation time.",
24
+ "items": {
25
+ "type": "string",
26
+ "minLength": 1
27
+ },
28
+ "minItems": 1,
29
+ "uniqueItems": true
30
+ },
31
+ "autonomy_default": {
32
+ "type": "string",
33
+ "description": "Initiative-level autonomy default chosen by the human at intake. One of `auto`, `review`, or `supervised`.",
34
+ "enum": ["auto", "review", "supervised"]
35
+ },
36
+ "correlation_id": {
37
+ "type": "string",
38
+ "description": "The initiative-level correlation ID minted during intake (INIT-YYYY-NNNN). Matches the envelope `correlation_id` and the registry filename. Duplicated here for payload self-containment.",
39
+ "pattern": "^INIT-\\d{4}-\\d{4}$"
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://specfuse.dev/orchestrator/schemas/events/spec_issue_resolved.schema.json",
4
+ "title": "spec_issue_resolved event payload",
5
+ "$comment": "Provenance: authored 2026-04-25 in WU 4.5 (spec-issue-triage skill). Fields: original_issue_correlation_id (required, task-level FEAT-YYYY-NNNN/TNN — the correlation ID from the triggering task that filed the spec issue), affected_files (required, array of paths edited in /product/ to resolve the issue), resolution_summary (required, string describing what was fixed and why). Emitted by the specs agent after fixing a spec-content problem in /product/ and re-validating the affected spec. The event type is new in Phase 4; per-type schema is authored alongside.",
6
+ "description": "Payload shape for the `spec_issue_resolved` event emitted by the specs agent's spec-issue-triage skill when a spec issue filed by a downstream agent (component, QA) is resolved by editing spec content under `/product/`. The resolution is a spec-side fix — the specs agent edits the affected spec file, re-validates, and emits this event. The original inbox file is archived to `/inbox/spec-issue/processed/`, never deleted.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "original_issue_correlation_id",
11
+ "affected_files",
12
+ "resolution_summary"
13
+ ],
14
+ "properties": {
15
+ "original_issue_correlation_id": {
16
+ "type": "string",
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.",
18
+ "pattern": "^((FEAT-\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/T\\d{2}$"
19
+ },
20
+ "affected_files": {
21
+ "type": "array",
22
+ "description": "Paths of spec files edited under `/product/` to resolve the issue, relative to the product specs repo root (e.g. `product/specs/inventory-api-bookmarks.yaml`). At least one file must be affected — a resolution that touches no files is not a resolution.",
23
+ "items": {
24
+ "type": "string",
25
+ "minLength": 1
26
+ },
27
+ "minItems": 1,
28
+ "uniqueItems": true
29
+ },
30
+ "resolution_summary": {
31
+ "type": "string",
32
+ "description": "Human-readable summary of what was fixed and why. States the concrete change (e.g. 'Changed response status from 201 to 200 for the idempotent repeat case on POST /bookmarks') — enough for downstream agents and the human to understand the fix without re-reading the diff.",
33
+ "minLength": 1
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://specfuse.dev/orchestrator/schemas/events/spec_issue_routed.schema.json",
4
+ "title": "spec_issue_routed event payload",
5
+ "$comment": "Provenance: authored 2026-04-25 in WU 4.5 (spec-issue-triage skill). Fields: original_issue_correlation_id (required, task-level FEAT-YYYY-NNNN/TNN — the correlation ID from the triggering task that filed the spec issue), target_project (required, owner/repo of the generator project the issue was filed against), filed_issue_reference (required, GitHub issue reference in owner/repo#N format). Emitted by the specs agent after determining a spec issue is a generator-template problem and filing a GitHub issue against the generator project. The event type is new in Phase 4; per-type schema is authored alongside.",
6
+ "description": "Payload shape for the `spec_issue_routed` event emitted by the specs agent's spec-issue-triage skill when a spec issue filed by a downstream agent (component, QA) is determined to be a generator-template problem rather than a spec-content problem. The specs agent files a GitHub issue against the generator project and emits this event. The original inbox file is archived to `/inbox/spec-issue/processed/`, never deleted. The generator feedback loop (Phase 5) will consume these issues; Phase 4 only files them.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "original_issue_correlation_id",
11
+ "target_project",
12
+ "filed_issue_reference"
13
+ ],
14
+ "properties": {
15
+ "original_issue_correlation_id": {
16
+ "type": "string",
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.",
18
+ "pattern": "^((FEAT-\\d{4}-\\d{4})|(INIT-\\d{4}-\\d{4}/F\\d{2}))/T\\d{2}$"
19
+ },
20
+ "target_project": {
21
+ "type": "string",
22
+ "description": "The GitHub `owner/repo` identifier of the generator project the issue was filed against (e.g. `acme/specfuse-generator`). Identifies which project owns the template that needs fixing.",
23
+ "pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$"
24
+ },
25
+ "filed_issue_reference": {
26
+ "type": "string",
27
+ "description": "The GitHub issue reference for the filed generator issue, in `owner/repo#N` format (e.g. `acme/specfuse-generator#42`). This is the durable reference that the generator feedback loop (Phase 5) will track.",
28
+ "pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+#\\d+$"
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,77 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://specfuse.dev/orchestrator/schemas/events/spec_validated.schema.json",
4
+ "title": "spec_validated event payload",
5
+ "$comment": "Provenance: authored 2026-04-25 in WU 4.4 (spec-validation skill). Fields: feature_correlation_id (required, FEAT-YYYY-NNNN), pass (required, boolean), spec_files_checked (required, array of paths), errors (required, array of structured error objects — empty on pass), validator_version (required, string). The spec_validated event type has existed in event.schema.json since the Phase 1 baseline; this per-type schema is additive, preserving the Phase 1 freeze contract.",
6
+ "description": "Payload shape for the `spec_validated` event emitted by the specs agent on every validation run (pass or fail). The event is append-only: re-validation after a fix produces a new `spec_validated` event; prior failures are preserved in the event log. Consumers use the most recent `spec_validated` event to determine current validation status. The `errors` array is empty on a clean pass and populated with structured error objects on failure — each object carries enough context (file, line, message, severity) for the spec-validation skill to produce actionable remediation guidance without the human needing to consult raw validator output.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "feature_correlation_id",
11
+ "pass",
12
+ "spec_files_checked",
13
+ "errors",
14
+ "validator_version"
15
+ ],
16
+ "properties": {
17
+ "feature_correlation_id": {
18
+ "type": "string",
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})?$"
21
+ },
22
+ "pass": {
23
+ "type": "boolean",
24
+ "description": "True if all spec files passed validation with zero errors (warnings do not block); false if any file produced at least one error-severity finding."
25
+ },
26
+ "spec_files_checked": {
27
+ "type": "array",
28
+ "description": "Paths of every spec file the validator checked, relative to the product specs repo root (e.g. `product/specs/inventory-api-bookmarks.yaml`, `product/features/FEAT-2026-0042.md`). Order matches the order the validator processed them. At least one file must be checked — a validation run against zero files is a configuration error, not a pass.",
29
+ "items": {
30
+ "type": "string",
31
+ "minLength": 1
32
+ },
33
+ "minItems": 1
34
+ },
35
+ "errors": {
36
+ "type": "array",
37
+ "description": "Structured error objects from the validator. Empty array when `pass` is true; at least one element when `pass` is false. Each object identifies the file, line, raw message, and severity of the finding. The spec-validation skill's interpretation table translates each raw message into actionable remediation guidance for the human.",
38
+ "items": {
39
+ "type": "object",
40
+ "additionalProperties": false,
41
+ "required": [
42
+ "file",
43
+ "line",
44
+ "message",
45
+ "severity"
46
+ ],
47
+ "properties": {
48
+ "file": {
49
+ "type": "string",
50
+ "description": "Path of the spec file that produced this error, relative to the product specs repo root.",
51
+ "minLength": 1
52
+ },
53
+ "line": {
54
+ "type": "integer",
55
+ "description": "Line number in the spec file where the error was detected. 0 when the validator does not report line-level granularity for this error type (e.g. a missing top-level field, a cross-file reference error).",
56
+ "minimum": 0
57
+ },
58
+ "message": {
59
+ "type": "string",
60
+ "description": "The validator's raw error message. Preserved verbatim for audit — the spec-validation skill interprets this into human-readable guidance but keeps the original for traceability.",
61
+ "minLength": 1
62
+ },
63
+ "severity": {
64
+ "type": "string",
65
+ "description": "Severity level reported by the validator. `error` blocks validation (causes `pass: false`); `warning` is advisory and does not block — the validation can still pass with warnings.",
66
+ "enum": ["error", "warning"]
67
+ }
68
+ }
69
+ }
70
+ },
71
+ "validator_version": {
72
+ "type": "string",
73
+ "description": "Version string of the Specfuse validator used for this run (e.g. `1.0.0`). Captured at invocation time via the validator's version-reporting mechanism, not cached across runs or eye-read from a config file.",
74
+ "minLength": 1
75
+ }
76
+ }
77
+ }
@@ -62,7 +62,7 @@ SPECFUSE_DIR = Path(".specfuse")
62
62
  REPO_ROOT = SPECFUSE_DIR.parent
63
63
  FEATURES_DIR = SPECFUSE_DIR / "features"
64
64
  VERIFICATION_PATH = SPECFUSE_DIR / "verification.yml"
65
- DRIVER_VERSION = "0.3.7"
65
+ DRIVER_VERSION = "0.3.9"
66
66
  # Oldest scaffold layout this driver can drive. init.sh stamps the scaffold's own
67
67
  # version into `.specfuse/VERSION`; check_scaffold_version() fails loud at startup if
68
68
  # the consumer's scaffold is older than this, pointing at `specfuse upgrade`. Bump
@@ -880,8 +880,19 @@ def prepare_feature(feat_fm: dict, feature_dir: "Path", feature_id: str) -> None
880
880
  """
881
881
  branch = feat_fm.get("branch")
882
882
  ensure_feature_branch(feat_fm, feature_dir)
883
- git("add", "--", str(feature_dir))
884
- staged = git("status", "--porcelain", "--", str(feature_dir))
883
+ # Stage the feature folder AND the roadmap surfaces draft-feature wrote but
884
+ # left uncommitted (#127). The roadmap row is part of the feature's initial
885
+ # state; if it isn't folded into this scaffold commit, the first WU's
886
+ # per-attempt `git reset --hard head_before` discards it and terminal close
887
+ # fails with roadmap_row_not_done ~20 minutes later. ensure_feature_branch
888
+ # has already vetted the dirty set down to the expected /pick-feature flips.
889
+ repo_root = Path(git("rev-parse", "--show-toplevel"))
890
+ add_paths = [str(feature_dir)]
891
+ for rel in (".specfuse/roadmap.md", ".specfuse/roadmap-archive.md"):
892
+ if (repo_root / rel).exists():
893
+ add_paths.append(str(repo_root / rel))
894
+ git("add", "--", *add_paths)
895
+ staged = git("status", "--porcelain", "--", *add_paths)
885
896
  if staged:
886
897
  git("commit", "-m", f"chore: scaffold feature {feature_id}")
887
898
  print(f"Prepared: committed feature folder on branch '{branch}'.")
@@ -2927,7 +2938,12 @@ def assert_terminal_flips_fired(
2927
2938
  if not parsed:
2928
2939
  return (
2929
2940
  False,
2930
- f"roadmap_row_not_done: row for {feature_id} not found",
2941
+ f"roadmap_row_not_done: row for {feature_id} not found in "
2942
+ f"{roadmap_path}. /draft-feature writes this row but leaves it "
2943
+ f"uncommitted; if it was dropped before dispatch, restore it and "
2944
+ f"re-commit. `specfuse-loop --prepare` folds the row into the "
2945
+ f"scaffold commit — dispatch through it so the row survives the "
2946
+ f"per-attempt reset.",
2931
2947
  )
2932
2948
  row_status = parsed["columns"].get("Status", "").strip()
2933
2949
  if row_status != "done":
@@ -164,6 +164,7 @@ _RULES_BLOCK = (
164
164
  "@.specfuse/rules/correlation-ids.md\n"
165
165
  "@.specfuse/rules/never-touch.md\n"
166
166
  "@.specfuse/rules/security-boundaries.md\n"
167
+ "@.specfuse/rules/verification-discipline.md\n"
167
168
  )
168
169
 
169
170
  _RULES_SENTINEL = "@.specfuse/rules/result-contract.md"
@@ -304,7 +305,7 @@ def _parse_version(v: str) -> tuple[int, int, int]:
304
305
 
305
306
 
306
307
  # Seed relpaths overlaid verbatim into .specfuse/<relpath> on upgrade
307
- _VERSIONED_OVERLAY_PREFIXES: tuple[str, ...] = ("templates/", "rules/", "docs/")
308
+ _VERSIONED_OVERLAY_PREFIXES: tuple[str, ...] = ("templates/", "rules/", "docs/", "schemas/")
308
309
  _VERSIONED_OVERLAY_EXACT: frozenset[str] = frozenset({"verification.yml.example", "VERSION"})
309
310
 
310
311
  # Versioned directories whose contents are pruned on upgrade