specfuse-loop 0.2.0__tar.gz → 0.3.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 (128) hide show
  1. {specfuse_loop-0.2.0/specfuse_loop.egg-info → specfuse_loop-0.3.0}/PKG-INFO +1 -1
  2. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/pyproject.toml +8 -4
  3. specfuse_loop-0.3.0/specfuse/loop/data/LEARNINGS.template.md +69 -0
  4. specfuse_loop-0.3.0/specfuse/loop/data/VERSION +1 -0
  5. specfuse_loop-0.3.0/specfuse/loop/data/docs/concepts/architecture-addendum-gates-and-iterative-planning.md +97 -0
  6. specfuse_loop-0.3.0/specfuse/loop/data/docs/concepts/ralph-lineage.md +66 -0
  7. specfuse_loop-0.3.0/specfuse/loop/data/docs/getting-started.md +188 -0
  8. specfuse_loop-0.3.0/specfuse/loop/data/docs/methodology.md +322 -0
  9. specfuse_loop-0.3.0/specfuse/loop/data/docs/skills.md +130 -0
  10. specfuse_loop-0.3.0/specfuse/loop/data/gitignore.snippet +11 -0
  11. specfuse_loop-0.3.0/specfuse/loop/data/roadmap.template.md +36 -0
  12. specfuse_loop-0.3.0/specfuse/loop/data/rules/correlation-ids.md +197 -0
  13. specfuse_loop-0.3.0/specfuse/loop/data/rules/never-touch.md +81 -0
  14. specfuse_loop-0.3.0/specfuse/loop/data/rules/result-contract.md +113 -0
  15. specfuse_loop-0.3.0/specfuse/loop/data/rules/security-boundaries.md +102 -0
  16. specfuse_loop-0.3.0/specfuse/loop/data/templates/GATE.template.md +43 -0
  17. specfuse_loop-0.3.0/specfuse/loop/data/templates/PLAN.template.md +74 -0
  18. specfuse_loop-0.3.0/specfuse/loop/data/templates/WU.template.md +103 -0
  19. specfuse_loop-0.3.0/specfuse/loop/data/verification.yml.example +53 -0
  20. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/lint_plan.py +25 -0
  21. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/loop.py +477 -32
  22. specfuse_loop-0.3.0/specfuse/loop/scaffold.py +630 -0
  23. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0/specfuse_loop.egg-info}/PKG-INFO +1 -1
  24. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse_loop.egg-info/SOURCES.txt +38 -0
  25. specfuse_loop-0.3.0/tests/test_arm_gate_edits_uncommitted.py +165 -0
  26. specfuse_loop-0.3.0/tests/test_autosync.py +200 -0
  27. specfuse_loop-0.3.0/tests/test_autosync_consent.py +294 -0
  28. specfuse_loop-0.3.0/tests/test_autosync_firstrun.py +146 -0
  29. specfuse_loop-0.3.0/tests/test_autosync_plugin.py +245 -0
  30. specfuse_loop-0.3.0/tests/test_bookkeeping_commit_crash_run.py +121 -0
  31. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_closing_deliverable_guard.py +55 -0
  32. specfuse_loop-0.3.0/tests/test_doctor.py +269 -0
  33. specfuse_loop-0.3.0/tests/test_init_integration.py +389 -0
  34. specfuse_loop-0.3.0/tests/test_leak_findings_redaction.py +109 -0
  35. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_leak_scan.py +9 -0
  36. specfuse_loop-0.3.0/tests/test_lint_bare_produces_path.py +153 -0
  37. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_orchestration.py +3 -3
  38. specfuse_loop-0.3.0/tests/test_migrate_legacy.py +268 -0
  39. specfuse_loop-0.3.0/tests/test_scaffold_data_in_sync.py +104 -0
  40. specfuse_loop-0.3.0/tests/test_scaffold_docs.py +101 -0
  41. specfuse_loop-0.3.0/tests/test_scaffold_init.py +133 -0
  42. specfuse_loop-0.3.0/tests/test_scaffold_manifest.py +144 -0
  43. specfuse_loop-0.3.0/tests/test_scaffold_resources.py +71 -0
  44. specfuse_loop-0.3.0/tests/test_scaffold_upgrade.py +184 -0
  45. specfuse_loop-0.3.0/tests/test_scaffold_wiring.py +325 -0
  46. specfuse_loop-0.3.0/tests/test_untracked_feature_folder.py +153 -0
  47. specfuse_loop-0.3.0/tests/test_upgrade_integration.py +391 -0
  48. specfuse_loop-0.3.0/tests/test_version_skew.py +115 -0
  49. specfuse_loop-0.2.0/tests/test_version_skew.py +0 -95
  50. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/LICENSE +0 -0
  51. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/NOTICE +0 -0
  52. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/README.md +0 -0
  53. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/setup.cfg +0 -0
  54. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/__init__.py +0 -0
  55. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/_miniyaml.py +0 -0
  56. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/adopt_feature.py +0 -0
  57. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/gate_eval.py +0 -0
  58. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/gh_backend.py +0 -0
  59. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/gh_features.py +0 -0
  60. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse/loop/validate_event.py +0 -0
  61. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse_loop.egg-info/dependency_links.txt +0 -0
  62. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse_loop.egg-info/entry_points.txt +0 -0
  63. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse_loop.egg-info/requires.txt +0 -0
  64. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/specfuse_loop.egg-info/top_level.txt +0 -0
  65. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_adopt_feature.py +0 -0
  66. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_attempt_outcome_emission.py +0 -0
  67. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_backend.py +0 -0
  68. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_bookkeeping_commit_hook_crash.py +0 -0
  69. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_cost_tracking.py +0 -0
  70. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_deliverable_presence_gate.py +0 -0
  71. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_driver_integration.py +0 -0
  72. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_driver_lock.py +0 -0
  73. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_duration_tracking.py +0 -0
  74. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_empty_files_escalation.py +0 -0
  75. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_ensure_feature_branch.py +0 -0
  76. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_extra_gates.py +0 -0
  77. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_force_full_close.py +0 -0
  78. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_gate_eval.py +0 -0
  79. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_gate_eval_calibration.py +0 -0
  80. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_gate_eval_intermediate_wiring.py +0 -0
  81. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_gate_eval_terminal_wiring.py +0 -0
  82. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_gh_backend.py +0 -0
  83. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_gh_features.py +0 -0
  84. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_git_env_isolation.py +0 -0
  85. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_hashed_denylist.py +0 -0
  86. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_hashed_denylist_ci.py +0 -0
  87. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_leak_scan_content.py +0 -0
  88. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_legacy_4wu_terminal_flips.py +0 -0
  89. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lifecycle_integration.py +0 -0
  90. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_close_intermediate.py +0 -0
  91. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_close_wu.py +0 -0
  92. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_correlation_id.py +0 -0
  93. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_correlation_id_close_intermediate.py +0 -0
  94. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_oracle_env.py +0 -0
  95. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_plan_errors.py +0 -0
  96. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_plan_next_draft.py +0 -0
  97. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_produces_driver_helper.py +0 -0
  98. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_sections.py +0 -0
  99. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_lint_task_graph_yaml_selection.py +0 -0
  100. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_auto_archive.py +0 -0
  101. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_caveman_preamble.py +0 -0
  102. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_close_intermediate.py +0 -0
  103. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_defaults_by_type.py +0 -0
  104. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_effort.py +0 -0
  105. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_failure_note_cap.py +0 -0
  106. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_files_changed_guard.py +0 -0
  107. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_gate_budget.py +0 -0
  108. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_model_alias.py +0 -0
  109. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_post_pass_invariant.py +0 -0
  110. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_reset_preserving_events.py +0 -0
  111. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_smoke_runner.py +0 -0
  112. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_unsandboxed.py +0 -0
  113. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_loop_zero_token_guard.py +0 -0
  114. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_miniyaml_equivalence.py +0 -0
  115. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_miniyaml_negative.py +0 -0
  116. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_planned_cost_lint.py +0 -0
  117. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_produces_field.py +0 -0
  118. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_result_block.py +0 -0
  119. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_roadmap_add_skill.py +0 -0
  120. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_roadmap_archive_skill.py +0 -0
  121. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_roadmap_row_parser.py +0 -0
  122. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_squash_commit_hook_crash.py +0 -0
  123. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_template_closing_shapes.py +0 -0
  124. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_terminal_flip_ownership.py +0 -0
  125. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_terminal_flips.py +0 -0
  126. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_validate_event.py +0 -0
  127. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_verdict_coupling.py +0 -0
  128. {specfuse_loop-0.2.0 → specfuse_loop-0.3.0}/tests/test_verify_empty_gate_set.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfuse-loop
3
- Version: 0.2.0
3
+ Version: 0.3.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.2.0"
7
+ version = "0.3.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"
@@ -56,8 +56,12 @@ specfuse-lint = "specfuse.loop.lint_plan:main"
56
56
 
57
57
  [tool.setuptools.packages.find]
58
58
  # Ship ONLY the specfuse namespace package. Without this scope, setuptools'
59
- # auto-discovery sweeps tests/, docs/, and scripts/ into the wheel. The driver's
60
- # data (templates, rules, features) lives in the consumer's `.specfuse/`, not in
61
- # the wheel, so the package is pure code.
59
+ # auto-discovery sweeps tests/, docs/, and scripts/ into the wheel.
62
60
  include = ["specfuse*"]
63
61
  namespaces = true
62
+
63
+ [tool.setuptools.package-data]
64
+ # Include the scaffold seed so `specfuse init` / `specfuse upgrade` can copy
65
+ # templates, rules, and other seed files out of the wheel without needing the
66
+ # loop source repo on disk.
67
+ "specfuse.loop" = ["data/**/*", "data/*"]
@@ -0,0 +1,69 @@
1
+ # LEARNINGS
2
+
3
+ Durable, reusable rules distilled from every gate's retrospective. The `lessons` work
4
+ unit appends here; planning reads here before detailing any new feature. This is the
5
+ feedback loop that makes each plan better than the last.
6
+
7
+ Append only. Phrase each entry as a rule that would change how a FUTURE work unit is
8
+ written or executed, not a one-off observation. De-duplicate against what is here.
9
+ Feature-specific observations stay in that feature's `RETROSPECTIVE.md` and are not
10
+ promoted here.
11
+
12
+ ## Format
13
+
14
+ ```
15
+ - [FEAT-YYYY-NNNN/G1] Implementation WUs must name the module a new route/handler
16
+ lives in; "add it to the router" cost a blocked attempt when no router existed yet.
17
+ ```
18
+
19
+ ## Entries
20
+
21
+ <!-- lessons work units append below this line -->
22
+ <!--
23
+ The entries below are GENERIC methodology lessons that ship with the scaffold —
24
+ they are about how to write and run work units, not about any one project. Your
25
+ project's own `lessons` work units append project-specific rules beneath them.
26
+ -->
27
+
28
+ - [meta/first-live-use] Scope a feature's acceptance criteria to the feature's
29
+ own footprint — its slug, the paths it creates or edits, the symbols it
30
+ introduces. Acceptance criteria that grep or scan the WHOLE repo will trip
31
+ on pre-existing, unrelated state and (correctly) cause the agent to emit
32
+ `status: blocked` even when the WU's own work is fine. Example failure mode:
33
+ a "no TODO comments anywhere in the tree" check that fires on legacy code
34
+ the WU never touches. Rule: bound checks to the feature's path prefixes
35
+ (e.g. `src/<slug>/**`) or to files the WU declares in `generated_surfaces` /
36
+ `files_changed`; repo-wide invariants belong in a separate hygiene WU or in
37
+ the repo's `code` gate set, not in a per-feature acceptance criterion.
38
+
39
+ - [meta/first-live-use] Name what the WU is expected to PRODUCE, not only what
40
+ it must NOT touch. The "Do not touch" section bounds the WU on one side;
41
+ without an equally-explicit "produces" list, an agent can helpfully write
42
+ files that should belong to a later WU (e.g. docs that were T92's job
43
+ showing up in T01's commit) without the verification gates objecting. Rule:
44
+ in addition to the "Do not touch" list, the WU's Acceptance criteria should
45
+ name the specific files/sections the WU is expected to author. A reviewer
46
+ reading the diff should be able to point at every changed file and find it
47
+ in either the WU's produces-list or the gate's verification output.
48
+
49
+ - [meta/first-live-use] The "hygiene WU" pattern — when a substantive WU
50
+ discovers a pre-existing bug in a path its "Do not touch" rule forbids
51
+ (typical case: shared module, infrastructure config, dependency version),
52
+ the right move is to insert a narrow hygiene WU EARLIER in the gate (or as
53
+ a precursor gate) that fixes only that issue. Not: loosen the blocked WU's
54
+ scope to permit the cross-cutting fix (muddies its boundary). Not: fix it
55
+ manually out-of-loop and pretend the gate ran clean (silent drift between
56
+ the methodology's history and git's). The hygiene WU should have a single,
57
+ obvious acceptance criterion and pass on its own verification; the original
58
+ blocked WU then runs after, unmodified.
59
+
60
+ - [meta/loop-driver-bugs] Driver bookkeeping (frontmatter status flips,
61
+ events.jsonl appends, per-attempt notes) must be committed if it should
62
+ survive across WUs — uncommitted writes are wiped by the inter-attempt
63
+ `git reset --hard`. Agent-work commits (per-WU squash) are separate from
64
+ bookkeeping commits (`chore(loop): ...`). When authoring WUs whose
65
+ verification commands themselves write to disk, remember the agent's
66
+ working tree is reset between failed attempts — scratch files written
67
+ during a failed attempt won't persist into the next attempt's prompt unless
68
+ the agent explicitly buffers them in the prompt-facing failure note the
69
+ driver hands to the next attempt.
@@ -0,0 +1 @@
1
+ 0.3.0
@@ -0,0 +1,97 @@
1
+ # Architecture Addendum — Gates and the iterative planning cycle (Model B)
2
+
3
+ > **Status: adopted (2026-06).** Gate placement is resolved as **Model B — gates live in the
4
+ > loop, per component, NOT in the orchestrator PM.** The gate cycle was proven on a real
5
+ > multi-gate feature (loop `FEAT-2026-0003`: plan-next drafted real, armable next gates across
6
+ > three cycles). This addendum records that decision and what it means for the orchestrator.
7
+ >
8
+ > **This supersedes the earlier Model-A proposal** (an earlier revision of this file that
9
+ > proposed folding gate identification / `plan-next` / per-gate `plan_review` into the PM agent as
10
+ > a `v1.7.0` behavioral change). That fold-in was **not adopted**; the PM does not gain gate
11
+ > machinery. See the orchestrator repo's `docs/gate-placement-proposal.md` (Model A vs B, decision
12
+ > criteria) and `docs/naming-convention.md` for the canonical contracts.
13
+
14
+ ---
15
+
16
+ ## 1. The decision
17
+
18
+ The orchestrator coordinates one level above a single-repo goal. An **initiative**
19
+ (`INIT-YYYY-NNNN`) is decomposed by the PM into a **`feature_graph`** of **features**
20
+ (`INIT-YYYY-NNNN/FNN`), each a single-repo goal dispatched to one component. **Each dispatched
21
+ implementation feature == a loop feature**: the receiving component's loop decomposes it into
22
+ **gates** and **work units** and grinds it through its gate cycle.
23
+
24
+ So gates are **internal to the loop**, not orchestrator state. The orchestrator owns
25
+ `initiative → feature` decomposition, cross-repo dependency ordering, and the spec/generated
26
+ interface contracts between features; it does **not** identify gates, run `plan-next`, or hold a
27
+ per-gate `plan_review`. The loop owns all of that, per [`methodology.md`](methodology.md).
28
+
29
+ **Why Model B (summary).** The loop is single-repo + edit-and-commit; codegen freezes the
30
+ cross-repo interface (generated `emit-*`/`on-*` contracts are immutable `_generated/`), so
31
+ component-loops grind hand-code against frozen boundaries and cannot break each other. This
32
+ dissolves the hardest part of Model A (predicting cross-repo gate boundaries inside the PM) and
33
+ keeps the gate cycle built once, in the loop. Full rationale + the rejected Model A:
34
+ `gate-placement-proposal.md`.
35
+
36
+ ## 2. What changed in the orchestrator (minimal — no PM gate machinery)
37
+
38
+ The orchestrator change is the **initiative/feature reframe**, already folded into
39
+ `orchestrator-architecture.md` §1A and `naming-convention.md`:
40
+
41
+ - **Vocabulary / IDs:** initiative → feature → gate → work unit; `INIT-YYYY-NNNN/FNN/TNN`
42
+ (legacy/component-local `FEAT-…/TNN`). Root token = origin.
43
+ - **State machines (unchanged in shape):** the "feature state machine" is the **initiative**
44
+ lifecycle; the "task state machine" is the **feature** lifecycle. Gates/WUs do **not** appear in
45
+ the orchestrator's state machines — they are loop-internal.
46
+ - **PM agent (reframed, not gate-extended):** `feature-decomposition` (was task-decomposition)
47
+ produces a `feature_graph`; `issue-drafting` files feature issues labelled by type;
48
+ `plan-review` reviews the `feature_graph`; `dependency-recomputation` (runtime `scripts/poller.py`)
49
+ flips features `pending → ready`. **No** gate identification, `plan-next`, or per-gate
50
+ `plan_review` in the PM — those were the Model-A additions and are dropped.
51
+ - **Dispatch by feature type:** `implementation` → the component-loop (loop GitHub feature-pick on
52
+ `specfuse:feature`); `qa_*` → the QA agent (`specfuse:qa-feature`), a distinct cross-repo role,
53
+ **not** a loop. QA is the exception to uniform-loop dispatch.
54
+ - **Per-gate autonomy / arming** lives in the loop (not the PM): autonomy flows orchestrator →
55
+ loop (`review`/`supervised` stop at each gate for a human arm; `auto` self-arms safe gates under
56
+ the methodology §9 conjunction). The merge gate stays human until the QA loop is trusted.
57
+
58
+ The orchestrator's earlier "no gates" behavior is therefore not changed by *adding* gates to it —
59
+ gates were placed in the loop instead.
60
+
61
+ ## 3. What the loop owns (the gate layer)
62
+
63
+ Per [`methodology.md`](methodology.md): the gate cycle (plan → execute → close → review&arm), the
64
+ four-type closing sequence (`retrospective → lessons → docs → plan-next`), `plan-next` drafting
65
+ the next gate (never arming it), `LEARNINGS.md`, and per-gate autonomy. These are **loop-internal**
66
+ to each dispatched feature; the orchestrator sees only the feature's overall state (via issue
67
+ labels) and its completion (PR merge → merge-watcher → `state:done`).
68
+
69
+ ## 4. Reconciliation with the orchestrator (the surface-specific seams)
70
+
71
+ Per the collaboration charter §2 / methodology §10, only these differ between surfaces:
72
+
73
+ | Concern | Loop (single-repo) | Orchestrator (multi-repo) |
74
+ |----------------|------------------------------------------|---------------------------------------------------|
75
+ | State backend | WU/GATE/PLAN frontmatter, git-tracked | GitHub issue labels + the initiative registry |
76
+ | Dispatch | driver shells out (`claude -p`) | poller routes by type → loop / QA agent |
77
+ | Branch / merge | one branch, squash per WU | branch + PR per **feature**, merge watcher |
78
+ | Report-back | RESULT block | `task_completed` event (+ `state:*` labels) via the loop's `GitHubBackend` |
79
+
80
+ The loop's `loop.py` is the reference for the orchestrator's poller (its dispatch/verify/retry/
81
+ gate-stop semantics decompose across the poller, PM dependency-recomputation, and the merge
82
+ watcher); the orchestrator does not import `loop.py`.
83
+
84
+ ## 5. Status of the old Model-A sections
85
+
86
+ The prior revision's §A.2–§A.11 (feature-state `in_progress → plan_review` oscillation, gate
87
+ skeleton in the PM, `plan-next` as a PM skill, per-gate `plan_review`, the auto-arm conjunction in
88
+ the PM, PM `v1.7.0`, the `gates`/`task.gate` frontmatter fields) described **Model A and are not
89
+ implemented.** The `feature-frontmatter.schema.json` `gates` array is not used by the orchestrator
90
+ (gates are loop-internal). If a future need arises to surface gate state at the orchestrator level,
91
+ re-open this addendum deliberately.
92
+
93
+ ## 6. Remaining (gated)
94
+
95
+ - `specfuse/methodology` extraction — once the gate-cycle contracts stop changing run-to-run
96
+ (charter §4; two contract fixes landed during the FEAT-2026-0003 dogfood — let them soak).
97
+ - Loop kit → `stable` in the orchestrator distribution manifest (same soak gate).
@@ -0,0 +1,66 @@
1
+ # Why the loop exists — lineage and positioning
2
+
3
+ ## The Ralph lineage
4
+
5
+ The loop descends from the "Ralph" technique: in its purest form, a bash loop
6
+ that feeds a prompt to a coding agent repeatedly until the work is done, with a
7
+ fresh context each iteration and durable state kept in files (git history, a
8
+ progress file, a task list) rather than in the context window. Its insight is
9
+ that for large work, *stubbornness plus fresh context* beats a single clever
10
+ pass — the loop is the hero, not the model.
11
+
12
+ Ralph's known weakness is the thinness of its task list: a bare list of TODOs
13
+ gives an agent nothing to enforce patterns against, so it drifts. The ecosystem's
14
+ answer to "that's too coarse for serious work" has been to make the units of work
15
+ granular and self-contained enough that ephemeral workers can pick them up,
16
+ execute, and hand off — orchestration of many such workers ("Gas Town"-style).
17
+
18
+ The Specfuse Loop is that idea with the planning rigor added back in. It keeps
19
+ Ralph's fresh-context-per-iteration property but moves it to **work-unit
20
+ granularity**, and it replaces the thin task list with the **Plan + Work Unit**
21
+ pattern: crisp work units with hard "do not touch" boundaries, explicit
22
+ acceptance criteria, and machine-checkable verification gates. The up-front
23
+ planning investment is precisely what earns the right to let execution run
24
+ unattended — the richer the unit, the longer the loop can safely run before a
25
+ human checkpoint.
26
+
27
+ Two things distinguish it from vanilla Ralph:
28
+
29
+ - **Verification is the exit oracle, not the agent's say-so.** The driver re-runs
30
+ the unit's gates and they decide done — eliminating Ralph's classic
31
+ premature-"done" failure.
32
+ - **Gates are human checkpoints by design.** The loop runs unattended *within* a
33
+ gate and stops *at* it. Reflection, a cross-feature learnings rollup, and
34
+ drafting the next batch happen systematically as the gate's closing sequence,
35
+ not when someone remembers to ask.
36
+
37
+ ## Where it sits in Specfuse
38
+
39
+ Specfuse is a methodology and an organization, not a single tool. Three
40
+ independently-adoptable projects live under it:
41
+
42
+ - **`specfuse/codegen`** turns OpenAPI / AsyncAPI / Arazzo specifications into
43
+ deterministic source code — the boilerplate no one should hand-write and no
44
+ agent should hallucinate.
45
+ - **`specfuse/loop`** (this project) executes the Plan + Work Unit pattern in a
46
+ single repository, with no specification required and no agent-coordination
47
+ overhead. The lightweight surface.
48
+ - **`specfuse/orchestrator`** coordinates specialized agents across many
49
+ component repositories from validated specifications — the heavyweight surface
50
+ for multi-repo, spec-first feature delivery.
51
+
52
+ The loop and the orchestrator are two execution surfaces of **one** methodology
53
+ (see [`methodology.md`](methodology.md)); they share the gate cycle, the
54
+ work-unit contract, the correlation-ID scheme, and the verification discipline.
55
+ The loop is the right home for work that lives in one repo or has no formal
56
+ spec; the orchestrator is the right home when the work genuinely spans repos and
57
+ is driven by specifications that `codegen` can turn into a stable foundation.
58
+
59
+ ## What it is not
60
+
61
+ - Not a general-purpose AI coding platform. It does one shape of work:
62
+ plan-driven, gated, fresh-context execution in a single repo.
63
+ - Not a replacement for human judgment. Every gate is a human checkpoint; the
64
+ loop keeps agents *inside* a loop, it does not remove the loop.
65
+ - Not a hosted service. It runs on your machine, against your repo, under your
66
+ accounts.
@@ -0,0 +1,188 @@
1
+ # Getting started
2
+
3
+ This walks you from an empty project to a feature delivered by the loop, then
4
+ shows what to do when a run halts. It assumes you've read the one-minute pitch in
5
+ the [README](../README.md); for the full contracts see
6
+ [`methodology.md`](methodology.md) and for the interactive operations see
7
+ [`skills.md`](skills.md).
8
+
9
+ The loop is stdlib-only Python plus Claude Code. There is no install step in your
10
+ target repo — `init.sh` copies a self-contained scaffold in.
11
+
12
+ ---
13
+
14
+ ## 1. Install the scaffold
15
+
16
+ From your checkout of `specfuse/loop`, point `init.sh` at the repo you want to
17
+ drive:
18
+
19
+ ```bash
20
+ ./init.sh /path/to/your-project
21
+ ```
22
+
23
+ This writes `.specfuse/` (templates, rules, skills, the driver, and the durable
24
+ docs) into your project and wires `.claude/` so Claude Code discovers the skills.
25
+ It refuses if `.specfuse/` already exists — use `./init.sh --upgrade` to update an
26
+ existing install in place without touching your authored files.
27
+
28
+ > **Don't gitignore `.specfuse/`.** The loop's durable state lives there and must
29
+ > be committed for the loop to work. `init.sh` warns if it detects the directory
30
+ > is ignored.
31
+
32
+ ## 2. Match verification to your stack
33
+
34
+ `init.sh` seeds `.specfuse/verification.yml`. Open it and make the `code` gate set
35
+ run *your* project's checks:
36
+
37
+ ```yaml
38
+ code:
39
+ - name: tests
40
+ command: "pytest -q"
41
+ - name: coverage
42
+ command: "coverage report --fail-under=90"
43
+ - name: lint
44
+ command: "ruff check ."
45
+ - name: security
46
+ command: "bandit -r src -ll"
47
+ ```
48
+
49
+ These commands are the **exit oracle**: the driver re-runs them itself after every
50
+ work unit and *they* decide whether the unit is done — the agent's own self-report
51
+ is advisory only ([methodology §5](methodology.md)). Keep this set in lock-step
52
+ with your GitHub branch protection, or an agent can pass locally and still be
53
+ unmergeable.
54
+
55
+ If your repo already has CI worth deriving from, run **`/derive-verification`** in
56
+ Claude Code instead of editing by hand — it inspects your CI and tooling and
57
+ drafts the file for you.
58
+
59
+ ## 3. Author your first feature
60
+
61
+ Two ways to create a feature folder under `.specfuse/features/`:
62
+
63
+ - **Interactively (recommended):** run **`/pick-feature`** to choose from your
64
+ roadmap, then **`/draft-feature`**. Draft-feature asks framing questions, then
65
+ proposes a gate skeleton and gate 1's work units, writing only on your accept.
66
+ - **By hand:** copy the worked example,
67
+ `.specfuse/features/FEAT-2026-0001-health-endpoint/`, and adapt it. It's a
68
+ deliberately small two-unit feature that exercises the whole loop. Or start from
69
+ the bare templates in `.specfuse/templates/`.
70
+
71
+ A feature folder holds:
72
+
73
+ | File | Owns | Who writes it |
74
+ |------|------|---------------|
75
+ | `PLAN.md` | the *shape*: gate order, WU membership, dependency edges, feature status | you / `draft-feature` (gate 1); `plan-next` (later gates) |
76
+ | `GATE-NN.md` | one gate's status and definition of done | you / the planner |
77
+ | `WU-*.md` | a single work unit: frontmatter + the prompt a fresh session receives | you / `draft-feature` / `plan-next` |
78
+
79
+ Then create the branch named in `PLAN.md`'s frontmatter (`branch:`).
80
+
81
+ ## 4. Validate before running
82
+
83
+ ```bash
84
+ python .specfuse/scripts/lint_plan.py .specfuse/features/FEAT-2026-0001-health-endpoint
85
+ ```
86
+
87
+ The linter checks structure: every WU has the five mandatory sections, the closing
88
+ sequence is present and well-formed, dependencies resolve, IDs are well-formed.
89
+ Fix anything it flags before dispatching — it's far cheaper than a failed
90
+ dispatch.
91
+
92
+ ## 5. Dry-run, then run
93
+
94
+ ```bash
95
+ python .specfuse/scripts/loop.py --dry-run # show the gate walked, in dep order, no dispatch
96
+ python .specfuse/scripts/loop.py # the real thing
97
+ ```
98
+
99
+ With no `--feature` flag the driver picks the single `active` feature. For each
100
+ ready work unit it:
101
+
102
+ 1. marks the WU `in_progress`,
103
+ 2. dispatches a **fresh** `claude -p` session with that unit's model and prompt,
104
+ 3. runs the unit's verification **itself** as the exit oracle,
105
+ 4. on pass, makes **one squashed commit** carrying the `Feature: FEAT-.../TNN`
106
+ trailer.
107
+
108
+ A failed gate is discarded and re-dispatched to a fresh session carrying the
109
+ failure evidence, up to three attempts, then the unit is escalated to
110
+ `blocked_human` and the gate halts.
111
+
112
+ > **One driver per working tree.** The driver holds an exclusive lock on
113
+ > `.specfuse/.loop.lock`. A second driver on the same checkout exits immediately.
114
+ > To run two features at once, use separate `git worktree` checkouts. `--dry-run`
115
+ > is exempt.
116
+
117
+ ## 6. The gate boundary — where you come back in
118
+
119
+ A gate ends with a **closing sequence** that runs automatically: it writes a
120
+ retrospective, promotes durable lessons to `LEARNINGS.md`, reconciles docs, and —
121
+ crucially — **drafts the next gate's work units** (as `draft`) so the next gate is
122
+ waiting for you to review.
123
+
124
+ Two things can happen at the boundary:
125
+
126
+ - **The gate auto-closes.** On a clean, on-plan gate the deterministic predicate
127
+ (`gate_eval.py`) closes it without a reflective session — but `plan-next` still
128
+ drafts the next gate, so the human review step still fires
129
+ ([methodology §3](methodology.md)).
130
+ - **The driver halts with `awaiting_review`.** The next gate's WUs are in `draft`
131
+ and the driver will refuse to execute them until you arm them. **Arming is the
132
+ human checkpoint and is deliberately not automated.**
133
+
134
+ Run **`/arm-gate`**. It walks each drafted WU — accept / revise / reject — flips
135
+ the ones you accept to `pending`, marks the finished gate `passed`, and prints the
136
+ resume command. Read the `GATE-NN-REVIEW.md` the planner wrote first: it's
137
+ weighted toward where the planner was *least* certain.
138
+
139
+ Then re-run `loop.py`. Repeat until the terminal gate is `done`.
140
+
141
+ ## 7. Wrap up
142
+
143
+ When the terminal gate is `done`, run **`/wrap-feature`**: it pushes the feature
144
+ branch, opens a PR, optionally watches CI, and points at the next pick. Then
145
+ **`/roadmap-archive`** moves the finished feature's detail out of the active
146
+ roadmap.
147
+
148
+ ---
149
+
150
+ ## Operating a running loop
151
+
152
+ The driver runs unattended within a gate, but real runs hit snags. The map:
153
+
154
+ | Symptom | What it means | Do this |
155
+ |---------|---------------|---------|
156
+ | Driver halts, a WU is `blocked_human` | A unit failed three attempts or hit an escalation trigger | Run **`/gate-status`** for a diagnosis (root cause, options, recommended action) |
157
+ | You fixed the blocker (creds, dep, spec) | The WU is still `blocked_human` | Run **`/unblock-wu`** to re-arm it (`blocked_human → pending`, attempts reset), then re-run |
158
+ | Driver exits "could not acquire lock" | Another driver owns this checkout | Find/stop the other driver, or use a separate `git worktree` |
159
+ | A gate is `awaiting_review` | Normal gate boundary | Run **`/arm-gate`** (§6) |
160
+ | The feature isn't worth finishing | — | Run **`/abandon-feature`** — flips every WU/gate/PLAN/roadmap surface cleanly |
161
+ | A WU "passed" but wrote no code | Hollow pass | Tighten the WU's acceptance criteria and verification; see [`authoring-work-units`](skills.md) |
162
+
163
+ **Where the durable state lives** (nothing important is in a context window):
164
+
165
+ - `PLAN.md` / `GATE-NN.md` / `WU-*.md` frontmatter — current status of everything.
166
+ - `events.jsonl` (per feature) — the event log; every dispatch emits an
167
+ `attempt_outcome`.
168
+ - `RETROSPECTIVE.md` — feature-local raw observations from each close.
169
+ - `LEARNINGS.md` (repo root of `.specfuse/`) — cross-feature durable lessons, read
170
+ at planning time so each plan is better than the last. Run
171
+ **`/learnings-suggest`** periodically to mine recurring failures into new
172
+ entries.
173
+
174
+ When in doubt after a halt, start with **`/gate-status`** — it reads all of the
175
+ above and tells you where you stand.
176
+
177
+ ## Fixing a bug (not a feature)
178
+
179
+ Bugs don't go through the feature methodology. Run **`/fix-bug`** with the issue
180
+ number or report: it's 1 bug = 1 branch = 1 PR, test-first. It refuses and
181
+ proposes promoting to a feature if the work turns out large or risky.
182
+
183
+ ## Next
184
+
185
+ - [`methodology.md`](methodology.md) — the full gate-cycle contract.
186
+ - [`skills.md`](skills.md) — every skill, by lifecycle phase.
187
+ - [`concepts/ralph-lineage.md`](concepts/ralph-lineage.md) — why the loop is
188
+ shaped the way it is.