@ai-dev-methodologies/rlp-desk 0.10.0 → 0.11.0

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.
@@ -1,704 +0,0 @@
1
- # Flywheel Redesign Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Replace the incomplete pivot step with a proper flywheel direction-review step that internalizes plan-ceo-review's core framework (premise challenge, forced alternatives, scope decisions, CEO cognitive patterns) within the campaign loop.
6
-
7
- **Architecture:** On Verifier FAIL, a fresh-context Flywheel agent runs BEFORE the next Worker. It reviews premises, proposes alternatives, decides scope (HOLD/PIVOT/REDUCE/EXPAND), rewrites the iteration contract, and records rejected directions. Worker then executes the updated contract. In tmux mode, Flywheel gets its own pane (leftmost). In agent mode, Leader calls Agent().
8
-
9
- **Tech Stack:** Node.js (ESM), zsh (init/run scripts), Claude Code Agent API
10
-
11
- **Spec:** `docs/superpowers/specs/2026-04-12-flywheel-redesign.md`
12
-
13
- ---
14
-
15
- ### Task 1: Remove existing pivot code
16
-
17
- **Files:**
18
- - Modify: `src/node/runner/campaign-main-loop.mjs` (remove pivot functions + wiring)
19
- - Modify: `src/node/run.mjs` (remove --pivot-mode, --pivot-model)
20
- - Modify: `src/scripts/init_ralph_desk.zsh` (remove pivot prompt template + cleanup refs)
21
- - Modify: `src/commands/rlp-desk.md` (remove pivot flags from options reference)
22
- - Delete: `tests/node/test-pivot-step.mjs`
23
-
24
- - [ ] **Step 1: Remove pivot functions from campaign-main-loop.mjs**
25
-
26
- Remove these functions entirely:
27
- - `shouldRunPivot()` (lines 402-408)
28
- - `buildPivotTriggerCmd()` (lines 410-412)
29
- - `dispatchPivot()` (lines 414-425)
30
-
31
- Remove pivot paths from `buildPaths()`:
32
- ```javascript
33
- // DELETE these two lines:
34
- pivotPromptFile: path.join(deskRoot, 'prompts', `${slug}.pivot.prompt.md`),
35
- pivotSignalFile: path.join(deskRoot, 'memos', `${slug}-pivot-signal.json`),
36
- ```
37
-
38
- Remove pivot wiring in the main loop (around line 539):
39
- ```javascript
40
- // DELETE this entire block:
41
- if (shouldRunPivot(options.pivotMode ?? 'off', state, lastVerdict)) {
42
- state.phase = 'pivot';
43
- // ... through to closing }
44
- }
45
- ```
46
-
47
- Remove `lastVerdict` variable declaration and assignment (lines 474, 608).
48
-
49
- - [ ] **Step 2: Remove pivot flags from run.mjs**
50
-
51
- Remove from defaults (lines 24-25):
52
- ```javascript
53
- // DELETE:
54
- pivotMode: 'off',
55
- pivotModel: 'opus',
56
- ```
57
-
58
- Remove from help text (lines 60-61):
59
- ```javascript
60
- // DELETE:
61
- ' --pivot-mode off|every|on-fail',
62
- ' --pivot-model MODEL',
63
- ```
64
-
65
- Remove from flag parser (lines 150-155):
66
- ```javascript
67
- // DELETE:
68
- case '--pivot-mode':
69
- options.pivotMode = consumeValue(args, index, token);
70
- index += 1;
71
- break;
72
- case '--pivot-model':
73
- options.pivotModel = consumeValue(args, index, token);
74
- index += 1;
75
- break;
76
- ```
77
-
78
- - [ ] **Step 3: Remove pivot prompt template from init_ralph_desk.zsh**
79
-
80
- Remove the entire pivot prompt section (starts with `# --- Pivot Prompt ---` around line 609, ends with the closing `fi` around line 660).
81
-
82
- Remove pivot files from re-execution cleanup (lines 282-283):
83
- ```bash
84
- # DELETE these from the cleanup list:
85
- "$DESK/memos/$SLUG-pivot-signal.json" \
86
- "$DESK/memos/$SLUG-pivot-review.md"
87
- ```
88
-
89
- Remove pivot prompt from prompt cleanup (line 298):
90
- ```bash
91
- # DELETE:
92
- "$DESK/prompts/$SLUG.pivot.prompt.md"
93
- ```
94
-
95
- Remove `--pivot-mode` and `--pivot-model` from `print_run_presets()` options reference.
96
-
97
- - [ ] **Step 4: Remove pivot flags from rlp-desk.md options reference**
98
-
99
- Remove `--pivot-mode` and `--pivot-model` lines from both codex-installed and codex-not-installed options blocks.
100
-
101
- - [ ] **Step 5: Delete test-pivot-step.mjs**
102
-
103
- ```bash
104
- rm tests/node/test-pivot-step.mjs
105
- ```
106
-
107
- - [ ] **Step 6: Fix us008 test regression**
108
-
109
- `tests/node/us008-cli-entrypoint.test.mjs` has a deepEqual check on RUN_DEFAULTS that includes pivotMode/pivotModel. Update to remove them.
110
-
111
- - [ ] **Step 7: Verify removal is clean**
112
-
113
- ```bash
114
- # No pivot references should remain (except blueprint doc):
115
- grep -r "pivot" src/ tests/node/ --include="*.mjs" --include="*.zsh" --include="*.md" -l
116
- # Expected: only docs/blueprints/blueprint-pivot-step.md
117
- ```
118
-
119
- ```bash
120
- zsh -n src/scripts/init_ralph_desk.zsh && echo "SYNTAX OK"
121
- node --test tests/node/us007-analytics-reporting.test.mjs
122
- node --test tests/node/us008-cli-entrypoint.test.mjs
123
- node --test tests/node/test-sv-report.mjs
124
- ```
125
-
126
- - [ ] **Step 8: Commit**
127
-
128
- ```bash
129
- git add -A
130
- git commit -m "refactor: remove incomplete pivot step implementation"
131
- ```
132
-
133
- ---
134
-
135
- ### Task 2: Add flywheel CLI flags + shouldRunFlywheel logic
136
-
137
- **Files:**
138
- - Modify: `src/node/run.mjs`
139
- - Modify: `src/node/runner/campaign-main-loop.mjs`
140
- - Create: `tests/node/test-flywheel.mjs`
141
-
142
- - [ ] **Step 1: Write failing tests**
143
-
144
- Create `tests/node/test-flywheel.mjs`:
145
-
146
- ```javascript
147
- import test from 'node:test';
148
- import assert from 'node:assert/strict';
149
-
150
- test('T1: shouldRunFlywheel returns false when flywheel=off', async () => {
151
- const { shouldRunFlywheel } = await import('../../src/node/runner/campaign-main-loop.mjs');
152
- assert.equal(shouldRunFlywheel('off', { consecutive_failures: 3 }), false);
153
- });
154
-
155
- test('T2: shouldRunFlywheel returns true when flywheel=on-fail and consecutive_failures > 0', async () => {
156
- const { shouldRunFlywheel } = await import('../../src/node/runner/campaign-main-loop.mjs');
157
- assert.equal(shouldRunFlywheel('on-fail', { consecutive_failures: 1 }), true);
158
- });
159
-
160
- test('T3: shouldRunFlywheel returns false when flywheel=on-fail and consecutive_failures=0', async () => {
161
- const { shouldRunFlywheel } = await import('../../src/node/runner/campaign-main-loop.mjs');
162
- assert.equal(shouldRunFlywheel('on-fail', { consecutive_failures: 0 }), false);
163
- });
164
- ```
165
-
166
- - [ ] **Step 2: Run tests to verify they fail**
167
-
168
- ```bash
169
- node --test tests/node/test-flywheel.mjs
170
- ```
171
- Expected: FAIL (shouldRunFlywheel not exported)
172
-
173
- - [ ] **Step 3: Implement shouldRunFlywheel + CLI flags**
174
-
175
- In `src/node/runner/campaign-main-loop.mjs`, add:
176
- ```javascript
177
- export function shouldRunFlywheel(flywheelMode, state) {
178
- if (flywheelMode === 'off') return false;
179
- if (flywheelMode === 'on-fail' && (state.consecutive_failures ?? 0) > 0) return true;
180
- return false;
181
- }
182
- ```
183
-
184
- In `src/node/run.mjs`, add defaults:
185
- ```javascript
186
- flywheel: 'off',
187
- flywheelModel: 'opus',
188
- ```
189
-
190
- Add to help text:
191
- ```javascript
192
- ' --flywheel off|on-fail',
193
- ' --flywheel-model MODEL',
194
- ```
195
-
196
- Add to flag parser:
197
- ```javascript
198
- case '--flywheel':
199
- options.flywheel = consumeValue(args, index, token);
200
- index += 1;
201
- break;
202
- case '--flywheel-model':
203
- options.flywheelModel = consumeValue(args, index, token);
204
- index += 1;
205
- break;
206
- ```
207
-
208
- - [ ] **Step 4: Run tests to verify they pass**
209
-
210
- ```bash
211
- node --test tests/node/test-flywheel.mjs
212
- ```
213
- Expected: 3 PASS
214
-
215
- - [ ] **Step 5: Commit**
216
-
217
- ```bash
218
- git add src/node/run.mjs src/node/runner/campaign-main-loop.mjs tests/node/test-flywheel.mjs
219
- git commit -m "feat: add --flywheel flag and shouldRunFlywheel logic"
220
- ```
221
-
222
- ---
223
-
224
- ### Task 3: Flywheel prompt template in init_ralph_desk.zsh
225
-
226
- **Files:**
227
- - Modify: `src/scripts/init_ralph_desk.zsh`
228
- - Modify: `tests/node/test-flywheel.mjs` (add prompt tests)
229
-
230
- - [ ] **Step 1: Write failing tests**
231
-
232
- Add to `tests/node/test-flywheel.mjs`:
233
-
234
- ```javascript
235
- import fs from 'node:fs/promises';
236
- import path from 'node:path';
237
- import { fileURLToPath } from 'node:url';
238
-
239
- const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
240
-
241
- test('T4: init generates flywheel prompt with 6 review steps', async () => {
242
- const script = path.join(repoRoot, 'src', 'scripts', 'init_ralph_desk.zsh');
243
- const content = await fs.readFile(script, 'utf8');
244
- assert.match(content, /Premise Challenge/);
245
- assert.match(content, /Existing Code Leverage/);
246
- assert.match(content, /Ideal State Mapping/);
247
- assert.match(content, /Implementation Alternatives/);
248
- assert.match(content, /Scope Decision/);
249
- assert.match(content, /Contract Rewrite/);
250
- });
251
-
252
- test('T5: flywheel prompt contains 10 CEO cognitive patterns', async () => {
253
- const script = path.join(repoRoot, 'src', 'scripts', 'init_ralph_desk.zsh');
254
- const content = await fs.readFile(script, 'utf8');
255
- assert.match(content, /First-principles/);
256
- assert.match(content, /10x check/);
257
- assert.match(content, /Inversion/);
258
- assert.match(content, /Simplicity bias/);
259
- assert.match(content, /User-back/);
260
- assert.match(content, /Time-value/);
261
- assert.match(content, /Sunk cost immunity/);
262
- assert.match(content, /Blast radius/);
263
- assert.match(content, /Reversibility/);
264
- assert.match(content, /Evidence > opinion/);
265
- });
266
-
267
- test('T6: flywheel prompt contains 4 scope decisions', async () => {
268
- const script = path.join(repoRoot, 'src', 'scripts', 'init_ralph_desk.zsh');
269
- const content = await fs.readFile(script, 'utf8');
270
- assert.match(content, /HOLD.*current approach/i);
271
- assert.match(content, /PIVOT.*alternative/i);
272
- assert.match(content, /REDUCE.*simplif/i);
273
- assert.match(content, /EXPAND.*missing/i);
274
- });
275
-
276
- test('T7: flywheel prompt writes to flywheel-signal.json', async () => {
277
- const script = path.join(repoRoot, 'src', 'scripts', 'init_ralph_desk.zsh');
278
- const content = await fs.readFile(script, 'utf8');
279
- assert.match(content, /flywheel-signal\.json/);
280
- });
281
-
282
- test('T8: flywheel prompt records rejected directions', async () => {
283
- const script = path.join(repoRoot, 'src', 'scripts', 'init_ralph_desk.zsh');
284
- const content = await fs.readFile(script, 'utf8');
285
- assert.match(content, /Rejected Directions/);
286
- });
287
- ```
288
-
289
- - [ ] **Step 2: Run tests to verify they fail**
290
-
291
- ```bash
292
- node --test tests/node/test-flywheel.mjs
293
- ```
294
- Expected: T1-T3 PASS, T4-T8 FAIL
295
-
296
- - [ ] **Step 3: Add flywheel prompt template to init_ralph_desk.zsh**
297
-
298
- After the Verifier prompt section, add:
299
-
300
- ```bash
301
- # --- Flywheel Prompt ---
302
- F="$DESK/prompts/$SLUG.flywheel.prompt.md"
303
- if [[ ! -f "$F" ]]; then
304
- cat > "$F" <<'FLYWHEEL_EOF'
305
- # Flywheel Direction Review
306
-
307
- You are an independent direction reviewer with fresh context. After a Worker iteration failed verification, you decide whether the current approach should continue, pivot, or change scope.
308
-
309
- ## Context Files
310
- Read these in order:
311
- 1. Campaign Memory: {DESK}/memos/{SLUG}-memory.md — especially Next Iteration Contract, Key Decisions, Rejected Directions
312
- 2. PRD: {DESK}/plans/prd-{SLUG}.md — acceptance criteria
313
- 3. Done Claim: {DESK}/memos/{SLUG}-done-claim.json — what Worker actually did
314
- 4. Verify Verdict: {DESK}/memos/{SLUG}-verify-verdict.json — why Verifier failed it
315
- 5. Latest Context: {DESK}/context/{SLUG}-latest.md — current state
316
-
317
- ## CEO Cognitive Patterns (apply throughout your review)
318
- 1. First-principles — ignore convention, start from the problem itself
319
- 2. 10x check — can 2x effort yield 10x better result?
320
- 3. Inversion — what must be true for this approach to fail?
321
- 4. Simplicity bias — prefer simple over complex solutions
322
- 5. User-back — reason backwards from end-user experience
323
- 6. Time-value — does this direction change save 3+ iterations?
324
- 7. Sunk cost immunity — ignore what was already invested
325
- 8. Blast radius — assess impact scope of direction change
326
- 9. Reversibility — prefer easily reversible decisions
327
- 10. Evidence > opinion — judge only by this iteration's actual results
328
-
329
- ## Review Process
330
-
331
- ### Step 0A: Premise Challenge
332
- List every assumption the current approach depends on.
333
- For each assumption, state whether THIS iteration's evidence supports or contradicts it.
334
- - Supported: "Assumption X — SUPPORTED: [evidence from done-claim/verdict]"
335
- - Contradicted: "Assumption X — BROKEN: [evidence]. This means [implication]."
336
- If any premise is broken, PIVOT or REDUCE is likely the right call.
337
-
338
- ### Step 0B: Existing Code Leverage
339
- - Did the Worker miss reusable code that already exists in the project?
340
- - Would a different approach align better with existing patterns?
341
- - Check: are there utilities, helpers, or patterns the Worker could have used?
342
-
343
- ### Step 0C: Ideal State Mapping
344
- Describe what this US looks like when perfectly implemented (2-3 sentences).
345
- How far is the current approach from this ideal? What is the gap?
346
-
347
- ### Step 0D: Implementation Alternatives (MANDATORY)
348
- Propose at least 2 alternative approaches. For each:
349
- - Summary (1-2 sentences)
350
- - Effort: S (< 1 iteration) / M (1-2 iterations) / L (3+ iterations)
351
- - Risk: low / medium / high
352
- - Key tradeoff vs current approach
353
-
354
- Do NOT skip this step. Even if the current approach seems correct, articulate alternatives.
355
-
356
- ### Step 0E: Scope Decision
357
- Choose ONE. Justify with evidence from this iteration only:
358
- - **HOLD**: Premises valid, approach correct. Refine the contract with specific fixes: "[fix 1], [fix 2]"
359
- - **PIVOT**: Premise [X] broken. Switch to Alternative [A]. Reason: [evidence]
360
- - **REDUCE**: AC [N] too complex at current scope. Split into [parts] or simplify to [simpler version]
361
- - **EXPAND**: Missing prerequisite [Y] discovered. Add to contract: [what to add]
362
-
363
- ### Step 0F: Contract Rewrite
364
- Based on your decision, update campaign memory:
365
- 1. Rewrite "Next Iteration Contract" with the new direction
366
- 2. Append your decision and reasoning to "Key Decisions"
367
- 3. If rejecting an approach, append to "Rejected Directions" section:
368
- "DO NOT retry: [approach description]. Reason: [why it failed]. Evidence: [from iteration N]."
369
- The next Worker MUST read Rejected Directions before starting.
370
-
371
- ## Output Files
372
-
373
- 1. Write analysis to: {DESK}/memos/{SLUG}-flywheel-review.md
374
- 2. Update campaign memory: {DESK}/memos/{SLUG}-memory.md
375
- 3. Write signal: {DESK}/memos/{SLUG}-flywheel-signal.json
376
- Format: {"iteration": N, "decision": "hold|pivot|reduce|expand", "summary": "one line", "rejected_directions": ["approach X because Y"], "contract_updated": true, "timestamp": "ISO"}
377
- FLYWHEEL_EOF
378
-
379
- # Replace placeholders with actual paths
380
- sed -i '' "s|{DESK}|$DESK|g; s|{SLUG}|$SLUG|g" "$F"
381
-
382
- echo " + $F"
383
- else echo " · $F"; fi
384
- ```
385
-
386
- Also add flywheel files to re-execution cleanup and add `--flywheel` flags to `print_run_presets()` options reference.
387
-
388
- - [ ] **Step 4: Run tests to verify they pass**
389
-
390
- ```bash
391
- zsh -n src/scripts/init_ralph_desk.zsh && echo "SYNTAX OK"
392
- node --test tests/node/test-flywheel.mjs
393
- ```
394
- Expected: T1-T8 all PASS, syntax OK
395
-
396
- - [ ] **Step 5: Commit**
397
-
398
- ```bash
399
- git add src/scripts/init_ralph_desk.zsh tests/node/test-flywheel.mjs
400
- git commit -m "feat: add flywheel prompt template with 6-step CEO framework"
401
- ```
402
-
403
- ---
404
-
405
- ### Task 4: Wire flywheel into campaign-main-loop.mjs
406
-
407
- **Files:**
408
- - Modify: `src/node/runner/campaign-main-loop.mjs`
409
- - Modify: `tests/node/test-flywheel.mjs` (add wiring tests)
410
-
411
- - [ ] **Step 1: Write failing tests**
412
-
413
- Add to `tests/node/test-flywheel.mjs`:
414
-
415
- ```javascript
416
- test('T9: buildPaths includes flywheel paths', async () => {
417
- // buildPaths is not exported, so test indirectly via init+run
418
- const script = path.join(repoRoot, 'src', 'node', 'runner', 'campaign-main-loop.mjs');
419
- const content = await fs.readFile(script, 'utf8');
420
- assert.match(content, /flywheelPromptFile/);
421
- assert.match(content, /flywheelSignalFile/);
422
- });
423
-
424
- test('T10: flywheel dispatch exists for both tmux and agent modes', async () => {
425
- const script = path.join(repoRoot, 'src', 'node', 'runner', 'campaign-main-loop.mjs');
426
- const content = await fs.readFile(script, 'utf8');
427
- assert.match(content, /dispatchFlywheel/);
428
- assert.match(content, /phase.*flywheel/i);
429
- });
430
-
431
- test('T11: flywheel runs BEFORE worker in the loop', async () => {
432
- const script = path.join(repoRoot, 'src', 'node', 'runner', 'campaign-main-loop.mjs');
433
- const content = await fs.readFile(script, 'utf8');
434
- const flywheelPos = content.indexOf('shouldRunFlywheel');
435
- const workerPos = content.indexOf('dispatchWorker', flywheelPos);
436
- assert.ok(flywheelPos < workerPos, 'flywheel check must appear before worker dispatch');
437
- });
438
- ```
439
-
440
- - [ ] **Step 2: Run tests to verify they fail**
441
-
442
- ```bash
443
- node --test tests/node/test-flywheel.mjs
444
- ```
445
- Expected: T1-T8 PASS, T9-T11 FAIL
446
-
447
- - [ ] **Step 3: Implement flywheel wiring**
448
-
449
- In `campaign-main-loop.mjs`:
450
-
451
- Add to `buildPaths()`:
452
- ```javascript
453
- flywheelPromptFile: path.join(deskRoot, 'prompts', `${slug}.flywheel.prompt.md`),
454
- flywheelSignalFile: path.join(deskRoot, 'memos', `${slug}-flywheel-signal.json`),
455
- ```
456
-
457
- Add `buildFlywheelTriggerCmd()`:
458
- ```javascript
459
- function buildFlywheelTriggerCmd({ flywheelPromptFile, flywheelModel, rootDir }) {
460
- return `cd ${JSON.stringify(rootDir)} && DISABLE_OMC=1 claude --model ${flywheelModel} --no-mcp -p "$(cat ${JSON.stringify(flywheelPromptFile)})"`;
461
- }
462
- ```
463
-
464
- Add `dispatchFlywheel()`:
465
- ```javascript
466
- async function dispatchFlywheel({ paths, sendKeys, flywheelPaneId, flywheelModel, rootDir }) {
467
- const triggerCmd = buildFlywheelTriggerCmd({
468
- flywheelPromptFile: paths.flywheelPromptFile,
469
- flywheelModel,
470
- rootDir,
471
- });
472
- await sendKeys({ targetPaneId: flywheelPaneId, keys: triggerCmd });
473
- }
474
- ```
475
-
476
- In the main `while` loop, AFTER verdict handling and BEFORE `dispatchWorker`, insert:
477
- ```javascript
478
- // Flywheel direction review (runs BEFORE Worker)
479
- if (shouldRunFlywheel(options.flywheel ?? 'off', state)) {
480
- state.phase = 'flywheel';
481
- await writeStatus(paths, state, options.onStatusChange, options.now);
482
-
483
- await dispatchFlywheel({
484
- paths,
485
- sendKeys,
486
- flywheelPaneId: state.flywheel_pane_id ?? state.verifier_pane_id,
487
- flywheelModel: options.flywheelModel ?? 'opus',
488
- rootDir,
489
- });
490
-
491
- const flywheelSignal = await pollForSignal(paths.flywheelSignalFile, {
492
- mode: 'claude',
493
- paneId: state.flywheel_pane_id ?? state.verifier_pane_id,
494
- });
495
-
496
- state.last_flywheel_decision = flywheelSignal.decision;
497
- // Campaign memory already updated by flywheel agent
498
- // Clean signal file for next iteration
499
- await fs.unlink(paths.flywheelSignalFile).catch(() => {});
500
- }
501
-
502
- state.phase = 'worker';
503
- // ... existing dispatchWorker code
504
- ```
505
-
506
- - [ ] **Step 4: Run tests to verify they pass**
507
-
508
- ```bash
509
- node --test tests/node/test-flywheel.mjs
510
- ```
511
- Expected: T1-T11 all PASS
512
-
513
- - [ ] **Step 5: Run regression tests**
514
-
515
- ```bash
516
- node --test tests/node/us007-analytics-reporting.test.mjs
517
- node --test tests/node/us008-cli-entrypoint.test.mjs
518
- node --test tests/node/test-sv-report.mjs
519
- ```
520
- Expected: all PASS
521
-
522
- - [ ] **Step 6: Commit**
523
-
524
- ```bash
525
- git add src/node/runner/campaign-main-loop.mjs tests/node/test-flywheel.mjs
526
- git commit -m "feat: wire flywheel into campaign loop (before worker, after verify fail)"
527
- ```
528
-
529
- ---
530
-
531
- ### Task 5: 3-pane tmux layout + flywheel flags in docs
532
-
533
- **Files:**
534
- - Modify: `src/scripts/init_ralph_desk.zsh` (print_run_presets)
535
- - Modify: `src/commands/rlp-desk.md` (options reference + flywheel docs)
536
- - Modify: `src/node/runner/campaign-main-loop.mjs` (3rd pane creation)
537
- - Modify: `tests/node/test-flywheel.mjs` (add docs tests)
538
-
539
- - [ ] **Step 1: Write failing tests**
540
-
541
- Add to `tests/node/test-flywheel.mjs`:
542
-
543
- ```javascript
544
- test('T12: rlp-desk.md options reference includes flywheel flags', async () => {
545
- const content = await fs.readFile(path.join(repoRoot, 'src', 'commands', 'rlp-desk.md'), 'utf8');
546
- assert.match(content, /--flywheel off\|on-fail/);
547
- assert.match(content, /--flywheel-model MODEL/);
548
- });
549
-
550
- test('T13: init presets include flywheel in options reference', async () => {
551
- const content = await fs.readFile(path.join(repoRoot, 'src', 'scripts', 'init_ralph_desk.zsh'), 'utf8');
552
- assert.match(content, /--flywheel off\|on-fail/);
553
- assert.match(content, /--flywheel-model MODEL/);
554
- });
555
-
556
- test('T14: campaign-main-loop creates flywheel pane in tmux mode', async () => {
557
- const content = await fs.readFile(path.join(repoRoot, 'src', 'node', 'runner', 'campaign-main-loop.mjs'), 'utf8');
558
- assert.match(content, /flywheel_pane_id/);
559
- });
560
- ```
561
-
562
- - [ ] **Step 2: Run tests to verify they fail**
563
-
564
- ```bash
565
- node --test tests/node/test-flywheel.mjs
566
- ```
567
- Expected: T1-T11 PASS, T12-T14 FAIL
568
-
569
- - [ ] **Step 3: Add flywheel flags to options references**
570
-
571
- In `src/commands/rlp-desk.md`, add to BOTH codex-installed and codex-not-installed options blocks:
572
- ```
573
- # --flywheel off|on-fail direction review on fail (default: off)
574
- # --flywheel-model MODEL flywheel reviewer model (default: opus)
575
- ```
576
-
577
- In `src/scripts/init_ralph_desk.zsh` `print_run_presets()`, add to the options reference echo block:
578
- ```bash
579
- echo "# --flywheel off|on-fail direction review on fail (default: off)"
580
- echo "# --flywheel-model MODEL flywheel reviewer model (default: opus)"
581
- ```
582
-
583
- - [ ] **Step 4: Add 3rd pane creation in tmux mode**
584
-
585
- In `campaign-main-loop.mjs`, in the session creation block (around line 440):
586
- ```javascript
587
- state.flywheel_pane_id = await createPane({
588
- targetPaneId: session.leaderPaneId,
589
- layout: 'horizontal',
590
- });
591
- state.worker_pane_id = await createPane({
592
- targetPaneId: session.leaderPaneId,
593
- layout: 'horizontal',
594
- });
595
- state.verifier_pane_id = await createPane({
596
- targetPaneId: session.leaderPaneId,
597
- layout: 'vertical',
598
- });
599
- ```
600
-
601
- - [ ] **Step 5: Run tests to verify they pass**
602
-
603
- ```bash
604
- node --test tests/node/test-flywheel.mjs
605
- zsh -n src/scripts/init_ralph_desk.zsh && echo "SYNTAX OK"
606
- ```
607
- Expected: T1-T14 all PASS, syntax OK
608
-
609
- - [ ] **Step 6: Run full regression**
610
-
611
- ```bash
612
- node --test tests/node/*.mjs tests/node/*.test.mjs 2>&1 | tail -5
613
- ```
614
- Expected: 0 failures
615
-
616
- - [ ] **Step 7: Commit**
617
-
618
- ```bash
619
- git add -A
620
- git commit -m "feat: flywheel 3-pane tmux layout + flags in docs and presets"
621
- ```
622
-
623
- ---
624
-
625
- ### Task 6: Self-verification (3 scenarios)
626
-
627
- **Files:** none modified — verification only
628
-
629
- - [ ] **Step 1: Scenario LOW — flywheel off**
630
-
631
- ```bash
632
- SV_DIR=$(mktemp -d) && cd "$SV_DIR" && git init -q
633
- mkdir -p .claude/ralph-desk
634
- zsh /path/to/src/scripts/init_ralph_desk.zsh "sv-flywheel-off" "test"
635
- # Verify: flywheel prompt generated but loop unchanged when --flywheel off
636
- grep -q "flywheel" .claude/ralph-desk/prompts/sv-flywheel-off.flywheel.prompt.md && echo "PASS: prompt exists"
637
- # Verify no flywheel pane created when off (Agent mode default)
638
- rm -rf "$SV_DIR"
639
- ```
640
-
641
- - [ ] **Step 2: Scenario MEDIUM — flywheel on-fail + FAIL triggers flywheel**
642
-
643
- Run actual Worker → Verifier → Flywheel → Worker sequence:
644
- 1. Init campaign in test project
645
- 2. Worker agent implements (intentionally incomplete)
646
- 3. Verifier FAIL
647
- 4. Flywheel agent runs direction review
648
- 5. Verify: flywheel-signal.json written with decision
649
- 6. Verify: campaign memory updated with Key Decisions + Rejected Directions
650
- 7. Next Worker reads updated contract
651
-
652
- - [ ] **Step 3: Scenario CRITICAL — PIVOT decision propagation**
653
-
654
- 1. Force a PIVOT decision from flywheel
655
- 2. Verify: Rejected Directions section in memory has the old approach
656
- 3. Verify: Next Iteration Contract is rewritten (not just patched)
657
- 4. Verify: Next Worker's done-claim shows a different approach
658
-
659
- - [ ] **Step 4: Record results**
660
-
661
- All 3 scenarios must PASS. Record in commit message.
662
-
663
- - [ ] **Step 5: Final commit**
664
-
665
- ```bash
666
- git add -A
667
- git commit -m "feat: flywheel direction review — verified with 3 self-verification scenarios
668
-
669
- Flywheel adds a CEO-framework direction review step that runs after Verifier FAIL
670
- and before the next Worker. Internalizes premise challenge, forced alternatives,
671
- scope decisions (HOLD/PIVOT/REDUCE/EXPAND), and 10 CEO cognitive patterns.
672
-
673
- Self-verified: LOW (off mode), MEDIUM (on-fail trigger), CRITICAL (PIVOT propagation)."
674
- ```
675
-
676
- ---
677
-
678
- ### Task 7: Local sync + docs
679
-
680
- **Files:**
681
- - Sync all distributable files to `~/.claude/`
682
-
683
- - [ ] **Step 1: Local file sync**
684
-
685
- ```bash
686
- cp src/commands/rlp-desk.md ~/.claude/commands/rlp-desk.md
687
- cp src/governance.md ~/.claude/ralph-desk/governance.md
688
- cp src/scripts/init_ralph_desk.zsh ~/.claude/ralph-desk/init_ralph_desk.zsh
689
- cp src/scripts/run_ralph_desk.zsh ~/.claude/ralph-desk/run_ralph_desk.zsh
690
- cp src/scripts/lib_ralph_desk.zsh ~/.claude/ralph-desk/lib_ralph_desk.zsh
691
- cp README.md ~/.claude/ralph-desk/README.md
692
- ```
693
-
694
- - [ ] **Step 2: Verify sync**
695
-
696
- ```bash
697
- diff -q src/commands/rlp-desk.md ~/.claude/commands/rlp-desk.md
698
- diff -q src/governance.md ~/.claude/ralph-desk/governance.md
699
- diff -q src/scripts/init_ralph_desk.zsh ~/.claude/ralph-desk/init_ralph_desk.zsh
700
- diff -q src/scripts/run_ralph_desk.zsh ~/.claude/ralph-desk/run_ralph_desk.zsh
701
- diff -q src/scripts/lib_ralph_desk.zsh ~/.claude/ralph-desk/lib_ralph_desk.zsh
702
- diff -q README.md ~/.claude/ralph-desk/README.md
703
- ```
704
- All must produce no output.