spec-and-loop 3.3.3 → 3.3.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-and-loop",
3
- "version": "3.3.3",
3
+ "version": "3.3.4",
4
4
  "description": "OpenSpec + Ralph Loop integration for iterative development with opencode",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -31,6 +31,13 @@
31
31
  * explicit, safe BLOCKED_HANDOFF classes
32
32
  * --no-auto-resolve-handoffs
33
33
  * Disable auto-resolution even when enabled by env
34
+ * --no-self-heal Disable supervisor self-heal (env: RALPH_SELF_HEAL=0)
35
+ * --self-heal-max-tries <n> Supervisor tries per blocker (env: RALPH_SELF_HEAL_MAX_TRIES)
36
+ * --no-self-heal-downstream Disable downstream supervisor patches (env: RALPH_SELF_HEAL_DOWNSTREAM=0)
37
+ * --no-self-heal-hints Disable supervisor investigation hints (env: RALPH_SELF_HEAL_HINTS=0)
38
+ * --no-self-heal-log-access Disable supervisor log-path injection (env: RALPH_SELF_HEAL_LOG_ACCESS=0)
39
+ * --self-heal-verbose Enable supervisor debug logging (env: RALPH_SELF_HEAL_VERBOSE=1)
40
+ * --no-self-heal-verbose Disable supervisor debug logging, even with --verbose
34
41
  * --no-commit Suppress auto-commit
35
42
  * --model <name> Optional model override
36
43
  * --verbose Verbose output
@@ -69,6 +76,12 @@ function parseArgs(argv) {
69
76
  taskPromise: 'READY_FOR_NEXT_TASK',
70
77
  blockedHandoffPromise: 'BLOCKED_HANDOFF',
71
78
  autoResolveHandoffs: _envFlagDefaultEnabled(process.env.RALPH_AUTO_RESOLVE_HANDOFFS),
79
+ selfHeal: null,
80
+ selfHealMaxTries: null,
81
+ selfHealDownstream: null,
82
+ selfHealHints: null,
83
+ selfHealLogAccess: null,
84
+ selfHealVerbose: null,
72
85
  noCommit: false,
73
86
  model: '',
74
87
  verbose: false,
@@ -126,6 +139,27 @@ function parseArgs(argv) {
126
139
  case '--no-auto-resolve-handoffs':
127
140
  opts.autoResolveHandoffs = false;
128
141
  break;
142
+ case '--no-self-heal':
143
+ opts.selfHeal = false;
144
+ break;
145
+ case '--self-heal-max-tries':
146
+ opts.selfHealMaxTries = parseInt(args[++i], 10);
147
+ break;
148
+ case '--no-self-heal-downstream':
149
+ opts.selfHealDownstream = false;
150
+ break;
151
+ case '--no-self-heal-hints':
152
+ opts.selfHealHints = false;
153
+ break;
154
+ case '--no-self-heal-log-access':
155
+ opts.selfHealLogAccess = false;
156
+ break;
157
+ case '--self-heal-verbose':
158
+ opts.selfHealVerbose = true;
159
+ break;
160
+ case '--no-self-heal-verbose':
161
+ opts.selfHealVerbose = false;
162
+ break;
129
163
  case '--no-commit':
130
164
  opts.noCommit = true;
131
165
  break;
@@ -180,9 +214,16 @@ Options:
180
214
  --completion-promise <s> Completion promise string
181
215
  --task-promise <s> Task promise string
182
216
  --blocked-handoff-promise <s>
183
- Blocked-handoff promise string (default: BLOCKED_HANDOFF)
217
+ Blocked-handoff promise string (default: BLOCKED_HANDOFF)
184
218
  --auto-resolve-handoffs Enable bounded continuation for explicit safe handoffs
185
219
  --no-auto-resolve-handoffs Disable bounded continuation for explicit safe handoffs
220
+ --no-self-heal Disable supervisor self-heal (env: RALPH_SELF_HEAL=0)
221
+ --self-heal-max-tries <n> Supervisor tries per blocker (env: RALPH_SELF_HEAL_MAX_TRIES)
222
+ --no-self-heal-downstream Disable downstream supervisor patches (env: RALPH_SELF_HEAL_DOWNSTREAM=0)
223
+ --no-self-heal-hints Disable supervisor investigation hints (env: RALPH_SELF_HEAL_HINTS=0)
224
+ --no-self-heal-log-access Disable supervisor log-path injection (env: RALPH_SELF_HEAL_LOG_ACCESS=0)
225
+ --self-heal-verbose Enable supervisor debug logging (env: RALPH_SELF_HEAL_VERBOSE=1)
226
+ --no-self-heal-verbose Disable supervisor debug logging, even with --verbose
186
227
  --no-commit Suppress auto-commit
187
228
  --model <name> Model override
188
229
  --verbose Verbose output
@@ -243,6 +284,12 @@ async function main() {
243
284
  taskPromise: opts.taskPromise,
244
285
  blockedHandoffPromise: opts.blockedHandoffPromise,
245
286
  autoResolveHandoffs: opts.autoResolveHandoffs,
287
+ selfHeal: opts.selfHeal,
288
+ selfHealMaxTries: opts.selfHealMaxTries,
289
+ selfHealDownstream: opts.selfHealDownstream,
290
+ selfHealHints: opts.selfHealHints,
291
+ selfHealLogAccess: opts.selfHealLogAccess,
292
+ selfHealVerbose: opts.selfHealVerbose,
246
293
  noCommit: opts.noCommit,
247
294
  model: opts.model,
248
295
  verbose: opts.verbose,
@@ -130,6 +130,12 @@ CHANGE_NAME=""
130
130
  MAX_ITERATIONS=""
131
131
  NO_COMMIT=false
132
132
  AUTO_RESOLVE_HANDOFFS=""
133
+ SELF_HEAL=""
134
+ SELF_HEAL_MAX_TRIES=""
135
+ SELF_HEAL_DOWNSTREAM=""
136
+ SELF_HEAL_HINTS=""
137
+ SELF_HEAL_LOG_ACCESS=""
138
+ SELF_HEAL_VERBOSE=""
133
139
  SHOW_STATUS=false
134
140
  SHOW_VERSION=false
135
141
  ADD_CONTEXT=""
@@ -138,6 +144,12 @@ SUBCOMMAND=""
138
144
  ERROR_OCCURRED=false
139
145
  CLEANUP_IN_PROGRESS=false
140
146
 
147
+ # Tracks the per-run temp directory created by setup_output_capture so the
148
+ # EXIT trap can remove it. Without this each invocation leaves a stale
149
+ # `$TMPDIR/ralph-run-XXXXXX` directory behind and they accumulate quickly
150
+ # (we observed ~1k stale dirs on a single workstation).
151
+ RALPH_RUN_TEMP_DIR=""
152
+
141
153
  # Trap signals for proper cleanup
142
154
  cleanup() {
143
155
  # Prevent multiple cleanup calls
@@ -153,7 +165,16 @@ cleanup() {
153
165
  # 1. The mini Ralph runtime runs synchronously in the foreground
154
166
  # 2. Ctrl+C (SIGINT) naturally propagates to child processes
155
167
  # 3. The shell's process group handling ensures clean termination.
156
-
168
+
169
+ # Remove the per-run temp directory created by setup_output_capture.
170
+ # Path-shape guard: only delete dirs whose name starts with `ralph-run-`
171
+ # so an accidental empty/aliased value cannot wipe an unrelated path.
172
+ if [[ -n "$RALPH_RUN_TEMP_DIR" \
173
+ && -d "$RALPH_RUN_TEMP_DIR" \
174
+ && "$(basename "$RALPH_RUN_TEMP_DIR")" == ralph-run-* ]]; then
175
+ rm -rf "$RALPH_RUN_TEMP_DIR" 2>/dev/null || true
176
+ fi
177
+
157
178
  if [[ $exit_code -ne 0 ]]; then
158
179
  log_error "Script terminated with exit code: $exit_code"
159
180
  fi
@@ -190,6 +211,16 @@ OPTIONS:
190
211
  --auto-resolve-handoffs Enable bounded continuation for explicit safe handoffs
191
212
  --no-auto-resolve-handoffs
192
213
  Disable bounded continuation for explicit safe handoffs
214
+ --no-self-heal Disable supervisor self-heal (env: RALPH_SELF_HEAL=0)
215
+ --self-heal-max-tries <n>
216
+ Supervisor tries per blocker (env: RALPH_SELF_HEAL_MAX_TRIES)
217
+ --no-self-heal-downstream
218
+ Disable downstream supervisor patches (env: RALPH_SELF_HEAL_DOWNSTREAM=0)
219
+ --no-self-heal-hints Disable supervisor investigation hints (env: RALPH_SELF_HEAL_HINTS=0)
220
+ --no-self-heal-log-access
221
+ Disable supervisor log-path injection (env: RALPH_SELF_HEAL_LOG_ACCESS=0)
222
+ --self-heal-verbose Enable supervisor debug logging (env: RALPH_SELF_HEAL_VERBOSE=1)
223
+ --no-self-heal-verbose Disable supervisor debug logging, even with --verbose
193
224
  --verbose, -v Enable verbose mode for debugging
194
225
  --quiet Suppress the per-iteration progress stream
195
226
  --version Print the version and exit
@@ -244,6 +275,34 @@ parse_arguments() {
244
275
  AUTO_RESOLVE_HANDOFFS=false
245
276
  shift
246
277
  ;;
278
+ --no-self-heal)
279
+ SELF_HEAL=false
280
+ shift
281
+ ;;
282
+ --self-heal-max-tries)
283
+ SELF_HEAL_MAX_TRIES="$2"
284
+ shift 2
285
+ ;;
286
+ --no-self-heal-downstream)
287
+ SELF_HEAL_DOWNSTREAM=false
288
+ shift
289
+ ;;
290
+ --no-self-heal-hints)
291
+ SELF_HEAL_HINTS=false
292
+ shift
293
+ ;;
294
+ --no-self-heal-log-access)
295
+ SELF_HEAL_LOG_ACCESS=false
296
+ shift
297
+ ;;
298
+ --self-heal-verbose)
299
+ SELF_HEAL_VERBOSE=true
300
+ shift
301
+ ;;
302
+ --no-self-heal-verbose)
303
+ SELF_HEAL_VERBOSE=false
304
+ shift
305
+ ;;
247
306
  --verbose|-v)
248
307
  VERBOSE=true
249
308
  shift
@@ -928,7 +987,10 @@ setup_output_capture() {
928
987
  local output_dir
929
988
  output_dir=$(make_temp_dir "ralph-run")
930
989
  log_info "Output directory: $output_dir"
931
-
990
+
991
+ # Track for cleanup() so the EXIT trap can remove it.
992
+ RALPH_RUN_TEMP_DIR="$output_dir"
993
+
932
994
  # Store output directory path in Ralph directory for reference
933
995
  echo "$output_dir" > "$ralph_dir/.output_dir"
934
996
 
@@ -1024,6 +1086,32 @@ Do not create git commits yourself. The Ralph runner manages automatic task comm
1024
1086
  mini_ralph_args+=("--no-auto-resolve-handoffs")
1025
1087
  fi
1026
1088
 
1089
+ if [[ "$SELF_HEAL" == false ]]; then
1090
+ mini_ralph_args+=("--no-self-heal")
1091
+ fi
1092
+
1093
+ if [[ -n "$SELF_HEAL_MAX_TRIES" ]]; then
1094
+ mini_ralph_args+=("--self-heal-max-tries" "$SELF_HEAL_MAX_TRIES")
1095
+ fi
1096
+
1097
+ if [[ "$SELF_HEAL_DOWNSTREAM" == false ]]; then
1098
+ mini_ralph_args+=("--no-self-heal-downstream")
1099
+ fi
1100
+
1101
+ if [[ "$SELF_HEAL_HINTS" == false ]]; then
1102
+ mini_ralph_args+=("--no-self-heal-hints")
1103
+ fi
1104
+
1105
+ if [[ "$SELF_HEAL_LOG_ACCESS" == false ]]; then
1106
+ mini_ralph_args+=("--no-self-heal-log-access")
1107
+ fi
1108
+
1109
+ if [[ "$SELF_HEAL_VERBOSE" == true ]]; then
1110
+ mini_ralph_args+=("--self-heal-verbose")
1111
+ elif [[ "$SELF_HEAL_VERBOSE" == false ]]; then
1112
+ mini_ralph_args+=("--no-self-heal-verbose")
1113
+ fi
1114
+
1027
1115
  if [[ "$VERBOSE" == true ]]; then
1028
1116
  mini_ralph_args+=("--verbose")
1029
1117
  fi
@@ -1202,6 +1290,7 @@ rules:
1202
1290
  - Each task has one dominant outcome and one verification cluster
1203
1291
  - Use surgical, scope-targeted validation commands; reserve broad gates for pre-flight baselines or final integration tasks
1204
1292
  - Include explicit stop-and-hand-off conditions
1293
+ - Run the OPENSPEC-RALPH-BP "Pre-loop scope-handoff pre-scan" against tasks.md before handing it to ralph-run; remediate every finding (dangling file paths, missing sections, scope/verifier mismatches, unclassified pre-existing failures, subjective stop conditions, unflagged manual-only tasks, cross-task scope conflicts)
1205
1294
  design:
1206
1295
  - Do not leave core policy choices unresolved
1207
1296
  - Specify algorithms, config shapes, and failure semantics
@@ -1224,6 +1313,18 @@ Before generating any OpenSpec artifacts, you MUST:
1224
1313
  - Ensure tasks use the task template with objective done-when conditions
1225
1314
  - Ensure each task uses the narrowest verifier that proves its scope; use broad gates only with baseline classification or final integration tasks
1226
1315
  - Include explicit stop-and-hand-off conditions in every task
1316
+
1317
+ Before handing `tasks.md` to `ralph-run` (whether you just authored it or just edited it), you MUST run the **Pre-loop scope-handoff pre-scan** from `openspec/OPENSPEC-RALPH-BP.md` against every pending `- [ ]` task. For each pending task, statically verify:
1318
+
1319
+ 1. Every file path in `Scope:` / `Done when:` / `Stop and hand off if:` resolves on disk (`ls`, `git ls-files`).
1320
+ 2. Every referenced section/heading exists in its named document with the exact heading text (`rg "^## <heading>$" <file>`).
1321
+ 3. The verifier's actual reach matches the `Scope:` statement; broaden scope or narrow the verifier when they disagree.
1322
+ 4. Multi-file gates that may hit pre-existing failures enumerate them in a "Pre-existing unrelated failures" sub-section with file:line references and a "do not stop on these" clause.
1323
+ 5. `Stop and hand off if:` conditions are objective (grep-able evidence, exact class/selector names) — never subjective ("looks wrong", "cannot be explained").
1324
+ 6. Any task requiring human-in-browser verification, deployed-URL checks, or visual judgment is tagged `[manual]` in its title with an explicit "manual verification required — emit BLOCKED_HANDOFF with verification template" stop condition.
1325
+ 7. No two pending tasks claim ownership of the same file/route/symbol; no task's `Stop and hand off if:` would trigger on the normal completion of a later task.
1326
+
1327
+ Remediate every finding by editing `tasks.md` directly, then re-run `openspec validate <change>` before starting the loop. If you cannot remediate a finding (it requires a product/policy decision), surface it to the user instead of starting the loop.
1227
1328
  RALPH_AGENTS
1228
1329
  log_verbose "Updated $agents_file with Ralph Wiggum compliance section"
1229
1330
  else
@@ -0,0 +1,134 @@
1
+ # Supervisor Prompt
2
+
3
+ You are the supervisor agent for the embedded Ralph loop.
4
+
5
+ Your job is to repair `tasks.md` structure, not to edit source code. You may propose a replacement body for the current pending task and optional downstream task patches when they share the same structural cause. You may also emit read-only investigation hints for the implementer.
6
+
7
+ ## Required Template Variables
8
+
9
+ The runner renders this template with every variable below:
10
+
11
+ - `{{blocker_note}}`
12
+ - `{{current_task_number}}`
13
+ - `{{current_task_body}}`
14
+ - `{{downstream_tasks}}`
15
+ - `{{handoff_history}}`
16
+ - `{{recent_iterations}}`
17
+ - `{{try_index}}`
18
+ - `{{previous_supervisor_attempts}}`
19
+ - `{{openspec_config_rules}}`
20
+ - `{{ralph_authoring_rules}}`
21
+ - `{{change_proposal}}`
22
+ - `{{change_design}}`
23
+ - `{{run_stdout_log_path}}`
24
+ - `{{run_stderr_log_path}}`
25
+
26
+ `{{tasks_md_path}}` and `{{blocker_hash}}` are intentionally not provided.
27
+
28
+ ## Structured Inputs
29
+
30
+ ### Blocker Note
31
+
32
+ {{blocker_note}}
33
+
34
+ ### Current Task Number
35
+
36
+ {{current_task_number}}
37
+
38
+ ### Current Task Body
39
+
40
+ {{current_task_body}}
41
+
42
+ ### Downstream Tasks
43
+
44
+ {{downstream_tasks}}
45
+
46
+ ### Handoff History
47
+
48
+ {{handoff_history}}
49
+
50
+ ### Recent Iterations
51
+
52
+ {{recent_iterations}}
53
+
54
+ ### Supervisor Try Index
55
+
56
+ {{try_index}}
57
+
58
+ ### Previous Supervisor Attempts
59
+
60
+ {{previous_supervisor_attempts}}
61
+
62
+ ### OpenSpec Config Rules
63
+
64
+ {{openspec_config_rules}}
65
+
66
+ ### Ralph Authoring Rules
67
+
68
+ {{ralph_authoring_rules}}
69
+
70
+ ### Change Proposal
71
+
72
+ {{change_proposal}}
73
+
74
+ ### Change Design
75
+
76
+ {{change_design}}
77
+
78
+ ### Optional Run Log Paths
79
+
80
+ stdout: `{{run_stdout_log_path}}`
81
+
82
+ stderr: `{{run_stderr_log_path}}`
83
+
84
+ If those paths are non-empty, you may read at most the tail 8 KiB needed to disambiguate a blocker. Treat them as read-only.
85
+
86
+ ## Response Contract
87
+
88
+ Emit exactly one JSON object inside a fenced `supervisor-response` block.
89
+
90
+ - `current_task_patch`: object or `null`
91
+ - `downstream_patches`: array
92
+ - `investigation_hints`: array
93
+ - `summary`: string
94
+ - `downstream_rationale`: string
95
+
96
+ When `current_task_patch` is an object, it must contain:
97
+
98
+ - `task_number`: numbered task identifier such as `2.3`
99
+ - `new_body`: full replacement markdown for the target task body
100
+ - `rationale`: one paragraph explaining the repair
101
+
102
+ Each `downstream_patches[]` entry may contain:
103
+
104
+ - `task_number`: existing numbered task to modify
105
+ - `operation`: `modify`, `insert_before`, or `insert_after`
106
+ - `anchor_task_number`: required for insert operations
107
+ - `new_body`: replacement or inserted task markdown
108
+ - `rationale`: similarity rationale for the downstream patch
109
+
110
+ Each `investigation_hints[]` entry must contain:
111
+
112
+ - `path`: repository-relative file path to read
113
+ - `rationale`: read-only explanation for why the implementer should inspect it
114
+
115
+ ## Example Response
116
+
117
+ ```supervisor-response
118
+ {
119
+ "current_task_patch": {
120
+ "task_number": "2.3",
121
+ "new_body": "- [ ] 2.3 **Define and document the supervisor I/O contract (request fields + response JSON shape)**\n - Scope: `scripts/supervisor-prompt.md`, `lib/mini-ralph/supervisor.js` (parser stub only)\n - Change: Define the template variables and parser contract.\n - Done when:\n - `scripts/supervisor-prompt.md` documents the required variables\n - `lib/mini-ralph/supervisor.js` exports the parser stub\n - focused parser coverage passes\n - Stop and hand off if:\n - the response shape conflicts with the OpenCode surface",
122
+ "rationale": "Freeze the supervisor contract before later orchestration and prompt-rendering work."
123
+ },
124
+ "downstream_patches": [],
125
+ "investigation_hints": [
126
+ {
127
+ "path": "lib/mini-ralph/tasks.js",
128
+ "rationale": "Read task parsing helpers before changing task-number validation."
129
+ }
130
+ ],
131
+ "summary": "Define the supervisor prompt and parser contract so later supervisor tasks can build against a stable schema.",
132
+ "downstream_rationale": ""
133
+ }
134
+ ```