@ai-dev-methodologies/rlp-desk 0.13.1 → 0.14.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.
- package/README.md +13 -6
- package/docs/plans/spicy-booping-galaxy.md +175 -2
- package/package.json +3 -3
- package/scripts/postinstall.js +14 -5
- package/scripts/uninstall.js +5 -0
- package/src/commands/rlp-desk.md +19 -5
- package/src/node/run.mjs +131 -1
- package/src/node/runner/campaign-main-loop.mjs +6 -0
package/README.md
CHANGED
|
@@ -238,7 +238,7 @@ When all US pass individually, the final ALL verify runs **sequentially per-US**
|
|
|
238
238
|
|
|
239
239
|
| Option | Default | Description |
|
|
240
240
|
|--------|---------|-------------|
|
|
241
|
-
| `--mode agent\|tmux` | agent |
|
|
241
|
+
| `--mode agent\|tmux` | agent | tmux=zsh Leader (stable, production), agent=Node Leader (alpha) |
|
|
242
242
|
| `--worker-model MODEL` | haiku | Worker model. `name`=claude, `name:reasoning`=codex |
|
|
243
243
|
| `--lock-worker-model` | off | Disable auto model upgrade on failure |
|
|
244
244
|
| `--verifier-model MODEL` | sonnet | per-US verification model (lighter) |
|
|
@@ -277,10 +277,17 @@ The brainstorm phase evaluates complexity (US count, file scope, logic, dependen
|
|
|
277
277
|
|
|
278
278
|
RLP Desk supports two execution modes. Both honor the same governance protocol.
|
|
279
279
|
|
|
280
|
+
> **v0.14.0 status:** `--mode tmux` (zsh-backed) is the **stable, production** path
|
|
281
|
+
> with the full safety net (heartbeat, copy-mode guard, prompt-stall timeout,
|
|
282
|
+
> no-progress detection, claude model upgrade chain). `--mode agent` is **alpha**
|
|
283
|
+
> and ships without those features — the runner emits a stderr warning when
|
|
284
|
+
> agent mode is invoked. For long campaigns and BOS-style autonomous loops,
|
|
285
|
+
> use `--mode tmux`.
|
|
286
|
+
|
|
280
287
|
### Environment Compatibility
|
|
281
288
|
|
|
282
|
-
| Environment | Agent Mode | Tmux Mode |
|
|
283
|
-
|
|
289
|
+
| Environment | Agent Mode (alpha) | Tmux Mode (stable) |
|
|
290
|
+
|-------------|--------------------|--------------------|
|
|
284
291
|
| Claude Code (any terminal) | **Works** | Requires tmux |
|
|
285
292
|
| Inside tmux session | **Works** | **Works** — panes split in current window |
|
|
286
293
|
| Outside tmux session | **Works** | **Rejected** — "start tmux first" |
|
|
@@ -289,9 +296,9 @@ RLP Desk supports two execution modes. Both honor the same governance protocol.
|
|
|
289
296
|
|
|
290
297
|
| Need | Use |
|
|
291
298
|
|------|-----|
|
|
292
|
-
|
|
|
293
|
-
|
|
|
294
|
-
|
|
|
299
|
+
| Production / autonomous campaigns | `--mode tmux` (stable) |
|
|
300
|
+
| Long campaigns, CI, overnight runs | `--mode tmux` (stable) |
|
|
301
|
+
| Quick interactive exploration inside Claude Code | `--mode agent` (alpha — Node-native) |
|
|
295
302
|
|
|
296
303
|
### Agent Mode (default) — "Smart Mode"
|
|
297
304
|
|
|
@@ -1,8 +1,181 @@
|
|
|
1
|
-
# Plan —
|
|
1
|
+
# Plan — v0.14.0 Recovery: 원래 의도대로 동작 회복 (zsh primary for tmux)
|
|
2
|
+
|
|
3
|
+
> **상위 우선순위 plan. 아래 v0.13.0 plan은 history reference로 보존.**
|
|
4
|
+
> **Trigger**: 사용자 평가 — "rlp-desk가 못 쓸 폐급, 통제 불가능 수준". v0.13.0/v0.13.1 fix는 빙산 일각.
|
|
5
|
+
> **Target version**: 0.14.0
|
|
6
|
+
> **승인된 strategy**: 경로 A (zsh restoration as tmux primary; Node leader는 `--mode agent`만 담당)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## A. Context (v0.14.0)
|
|
11
|
+
|
|
12
|
+
### 문제 진단
|
|
13
|
+
|
|
14
|
+
2026-04-12 Node port 시점부터 v0.13.x까지, **Node leader가 zsh runner의 핵심 안전망 11개를 누락한 채 ship**. 사용자(BOS) 평가는 "통제 불가능". v0.13.0/v0.13.1 patch는 다음 2개만 해결:
|
|
15
|
+
1. `.claude/` sensitive prompt hang
|
|
16
|
+
2. detached session UX 회귀
|
|
17
|
+
|
|
18
|
+
**여전히 누락된 것** (file:line 인용):
|
|
19
|
+
| # | 기능 | zsh location | Node 상태 |
|
|
20
|
+
|---|------|--------------|----------|
|
|
21
|
+
| 1 | Copy-mode 가드 send-keys | `safe_send_keys` L976-1083 | 없음 (pane-manager.mjs:50-53 단순 send-keys) |
|
|
22
|
+
| 2 | Heartbeat 주기적 쓰기 + staleness 감지 | L1735-1750, L1158 | 없음 |
|
|
23
|
+
| 3 | No-progress 10분 byte-stasis 감지 | `check_no_progress` L2372-2420 | 없음 |
|
|
24
|
+
| 4 | Prompt-stall 5분 timeout | `check_prompt_stall` L2298-2370 | 없음 |
|
|
25
|
+
| 5 | Stale-context 3 consecutive unchanged iter | `check_stale_context` lib L1162-1179 | 없음 |
|
|
26
|
+
| 6 | Claude 모델 upgrade chain (haiku→sonnet→opus) | `get_next_model` lib L136-155 | 없음 (codex chain만) |
|
|
27
|
+
| 7 | LOCK_WORKER_MODEL flag 처리 | lib L197 | 없음 |
|
|
28
|
+
| 8 | Codex update prompt auto-dismiss | L1007-1011 | 없음 |
|
|
29
|
+
| 9 | Pane lifecycle cleanup | `cleanup_panes` L3310-3320 | 없음 (`_ensureTerminalSentinel` 부분만) |
|
|
30
|
+
| 10 | 사용자 pane-kill graceful detection | L134-150 | 없음 |
|
|
31
|
+
| 11 | Cleanup trap (C-c → /exit → kill-pane) | L1864-2014 | 부분만 |
|
|
32
|
+
|
|
33
|
+
### 핵심 결정
|
|
34
|
+
|
|
35
|
+
**zsh를 tmux mode primary path로 복원, Node leader는 `--mode agent`(LLM-driven orchestration) 단독 담당**.
|
|
36
|
+
|
|
37
|
+
**근거**:
|
|
38
|
+
- zsh runner는 v0.12.0 deprecation 전까지 6주+ production 검증.
|
|
39
|
+
- Node port의 누락 11개를 모두 port = 5-7일 + 새 회귀 위험. zsh 복원 = 1-2일 + 검증된 코드.
|
|
40
|
+
- Node leader는 LLM이 worker/verifier를 spawn하는 agent mode에 고유 가치 — tmux 기계적 orchestration은 zsh가 더 잘함.
|
|
41
|
+
- 사용자 즉시 회복 우선.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## B. Approach (6 Phases)
|
|
46
|
+
|
|
47
|
+
### Phase 1 — zsh deprecation 게이트 해제 (Day 1, 2시간)
|
|
48
|
+
|
|
49
|
+
**파일**: `src/scripts/run_ralph_desk.zsh`
|
|
50
|
+
- L69-90: `--flywheel`/`--with-self-verification`/`--flywheel-guard` hard-reject 블록 제거. zsh가 이 flag들을 다시 honor.
|
|
51
|
+
- L91 "deprecated" 메시지 제거.
|
|
52
|
+
- `RALPH_DESK_VERSION` 0.14.0으로 갱신.
|
|
53
|
+
|
|
54
|
+
### Phase 2 — Node `--mode tmux` → zsh subprocess 라우팅 (Day 1, 4시간)
|
|
55
|
+
|
|
56
|
+
**파일**: `src/node/run.mjs`
|
|
57
|
+
- `parseRunOptions()` 후 `runRunCommand()` 진입에서 `mode === 'tmux'` 분기:
|
|
58
|
+
- `~/.claude/ralph-desk/run_ralph_desk.zsh` 경로 확인 (postinstall이 sync 보장).
|
|
59
|
+
- 모든 옵션을 env vars로 변환 (`LOOP_NAME`, `WORKER_MODEL`, `FLYWHEEL`, `FLYWHEEL_GUARD`, `WITH_SELF_VERIFICATION`, `MAX_ITER`, `ITER_TIMEOUT`, `CB_THRESHOLD`, `CONSENSUS_*`, `LOCK_WORKER_MODEL` 등).
|
|
60
|
+
- `child_process.spawn('zsh', [zshPath], { env, stdio: 'inherit' })`로 위임.
|
|
61
|
+
- exit code 그대로 propagate.
|
|
62
|
+
- legacy detection (`detectLegacyDeskInRunMode`) 호출은 zsh spawn 전에 유지.
|
|
63
|
+
- claude+tmux warning 유지 (zsh도 같은 worker engine 분기에서 적용).
|
|
64
|
+
|
|
65
|
+
**파일**: `src/node/runner/campaign-main-loop.mjs`
|
|
66
|
+
- `run()` 진입에서 `options.mode === 'tmux'`일 때 가드: `throw new Error('tmux mode is delegated to zsh — invoke via run.mjs router')`. dead-code 표시 + 회귀 방지.
|
|
67
|
+
|
|
68
|
+
### Phase 3 — postinstall + install.sh가 zsh를 항상 sync (Day 1, 1시간)
|
|
69
|
+
|
|
70
|
+
**파일**: `scripts/postinstall.js`
|
|
71
|
+
- 현재 `legacyFiles` 배열로 zsh 3개 삭제 → **유지·sync로 변경**.
|
|
72
|
+
- `runtimeSources`에 `src/scripts/{init,run,lib}_ralph_desk.zsh` → `~/.claude/ralph-desk/` 추가 (또는 `scripts/` 하위 — install.sh와 일관성 결정).
|
|
73
|
+
- banner-aware sync.
|
|
74
|
+
|
|
75
|
+
**파일**: `tests/node/us008-cli-entrypoint.test.mjs:47`
|
|
76
|
+
- 기존 "removes legacy zsh scripts" 테스트 invert: zsh 3개가 install 후 존재 + spawnable 검증.
|
|
77
|
+
|
|
78
|
+
### Phase 4 — Node `--mode agent` 라벨링 (Day 2, 2시간)
|
|
79
|
+
|
|
80
|
+
**파일**: `src/node/run.mjs`, `src/commands/rlp-desk.md`, `README.md`
|
|
81
|
+
- `--mode agent` 진입 시 stderr 경고: "agent mode is alpha — for production use --mode tmux".
|
|
82
|
+
- README mode 표:
|
|
83
|
+
- `tmux` (stable, zsh-backed)
|
|
84
|
+
- `agent` (alpha, Node-native)
|
|
85
|
+
- v0.13.0/0.13.1 fix(`.claude/` 마이그레이션, prompt-detector, claude+tmux warning)는 모두 agent mode에서 잔존 — Node 단독 가치 보존.
|
|
86
|
+
|
|
87
|
+
### Phase 5 — 검증 시나리오 (Day 2-3, 1일)
|
|
88
|
+
|
|
89
|
+
**Self-verification gate (`tests/sv-self-verify-0.14.sh`)** — v0.13 시나리오 + 신규:
|
|
90
|
+
- L5.1 (CRITICAL) BOS 회귀: claude worker + tmux mode + 1 iter 완주 (실제 tmux session 생성, kill-session cleanup).
|
|
91
|
+
- L5.2 (CRITICAL) zsh subprocess routing: `--mode tmux` 호출이 `child_process.spawn('zsh', ...)`로 위임됐는지 mock 검증.
|
|
92
|
+
- L5.3 (CRITICAL) flag → env var conversion 단언 (모든 supported flag 1개씩).
|
|
93
|
+
- L5.4 (MEDIUM) zsh deprecation 게이트 제거 검증 (L69-90).
|
|
94
|
+
- L5.5 (MEDIUM) postinstall이 zsh 3개를 install (us008 invert).
|
|
95
|
+
- L5.6 (MEDIUM) `--mode agent` warning 출력 검증.
|
|
96
|
+
- L5.7 (UX) attached tmux 안에서 leader+worker+verifier panes 사용자 현재 window에 표시 (zsh L815-823 동작).
|
|
97
|
+
|
|
98
|
+
### Phase 6 — Ship (Day 3)
|
|
99
|
+
|
|
100
|
+
CLAUDE.md release workflow 그대로:
|
|
101
|
+
1. self-verification gate 17/17 PASS.
|
|
102
|
+
2. ralplan + codex review (기존 mandate).
|
|
103
|
+
3. version bump 0.14.0.
|
|
104
|
+
4. CHANGELOG: "Restore zsh as primary tmux runner. Node tmux delegates to validated zsh codepath. Node agent mode marked alpha."
|
|
105
|
+
5. commit + push + gh release + npm publish.
|
|
106
|
+
6. local sync banner-aware verify.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## C. v0.13.x에서 보존되는 것
|
|
111
|
+
|
|
112
|
+
- `.claude/ralph-desk/` → `.rlp-desk/` 경로 마이그레이션 (init mode auto-mv, run mode 안내) — zsh도 v0.13.0에서 이미 반영됨.
|
|
113
|
+
- `RLP_DESK_RUNTIME_DIR` env override.
|
|
114
|
+
- prompt-detector + signal-poller permission_prompt 감지 — agent mode 전용.
|
|
115
|
+
- BLOCK_TAGS.PERMISSION_PROMPT 상수.
|
|
116
|
+
- claude+tmux warning (run.mjs 진입 시).
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## D. v0.15.0+ 점진 port 백로그 (deferred)
|
|
121
|
+
|
|
122
|
+
**P0 (2주 내, agent mode parity 위해)**:
|
|
123
|
+
- heartbeat 메커니즘
|
|
124
|
+
- copy-mode 가드 send-keys
|
|
125
|
+
- prompt-stall 5분 timeout
|
|
126
|
+
- no-progress 10분 byte-stasis
|
|
127
|
+
|
|
128
|
+
**P1**:
|
|
129
|
+
- stale-context 감지
|
|
130
|
+
- claude 모델 upgrade chain (haiku→sonnet→opus)
|
|
131
|
+
- LOCK_WORKER_MODEL flag
|
|
132
|
+
|
|
133
|
+
**P2**:
|
|
134
|
+
- codex update prompt auto-dismiss
|
|
135
|
+
- pane lifecycle cleanup
|
|
136
|
+
- user-kill graceful detection
|
|
137
|
+
- cleanup trap full parity
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## E. Critical Files
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
src/scripts/run_ralph_desk.zsh # Phase 1 — deprecation 게이트 해제
|
|
145
|
+
src/scripts/lib_ralph_desk.zsh # 변경 없음 (zsh helpers 그대로)
|
|
146
|
+
src/scripts/init_ralph_desk.zsh # 변경 없음 (v0.13.0 마이그레이션 그대로)
|
|
147
|
+
src/node/run.mjs # Phase 2 — tmux mode 라우터
|
|
148
|
+
src/node/runner/campaign-main-loop.mjs # Phase 2 — tmux mode 가드
|
|
149
|
+
scripts/postinstall.js # Phase 3 — zsh sync 복원
|
|
150
|
+
tests/node/us008-cli-entrypoint.test.mjs # Phase 3 — invert
|
|
151
|
+
src/commands/rlp-desk.md # Phase 4 — mode 표 갱신
|
|
152
|
+
README.md # Phase 4 — stable/alpha 표시
|
|
153
|
+
package.json # Phase 6 — 0.14.0
|
|
154
|
+
tests/sv-self-verify-0.14.sh # Phase 5 — 신규 SV gate
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## F. 검증 (E2E)
|
|
160
|
+
|
|
161
|
+
1. **BOS 회귀 (CRITICAL)**: BOS 프로젝트 worktree에서 `node ~/.claude/ralph-desk/node/run.mjs run bos-phase-1 --mode tmux --worker-model sonnet --max-iter 1 --iter-timeout 600` → tmux session 생성, leader/worker/verifier panes 사용자 현재 window에 split, 1 iter sentinel write 성공, no permission prompt hang.
|
|
162
|
+
2. **Local sync**: `npm install` 후 `~/.claude/ralph-desk/run_ralph_desk.zsh` 존재 + banner.
|
|
163
|
+
3. **Backward compat**: v0.13.x mid-campaign 사용자 — `mv .claude/ralph-desk .rlp-desk` 안내 그대로.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## G. 기각된 대안
|
|
168
|
+
|
|
169
|
+
- **B (Node 전면 port)**: 11 features × 평균 3시간 + parity 회귀테스트 = 5-7일 + 신규 버그 위험. v0.15.0+ 점진 port로 deferred.
|
|
170
|
+
- **C (단독 라벨링)**: experimental 라벨만으로는 "통제 불가능" 즉시 해소 불가. 경로 A에 흡수.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
# v0.13.0 (HISTORY) — Claude worker `.claude/` sensitive prompt hang 수정
|
|
2
175
|
|
|
3
176
|
> **Source bug report**: `/Users/kyjin/dev/doul/bos/docs/exec-plans/active/2026-05-01-rlp-desk-bug-report.md`
|
|
4
177
|
> **Severity**: HIGH — `--mode tmux` + `--worker-model sonnet/haiku/opus` 조합에서 모든 campaign blocking
|
|
5
|
-
> **Target version**: 0.13.0 (breaking — project-local sentinel 경로 이동)
|
|
178
|
+
> **Target version**: 0.13.0 (breaking — project-local sentinel 경로 이동) — **SHIPPED**, but coverage was a sliver of the real failure surface (see v0.14.0 plan above).
|
|
6
179
|
|
|
7
180
|
---
|
|
8
181
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-dev-methodologies/rlp-desk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Fresh-context iterative loops for Claude Code
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"description": "Fresh-context iterative loops for Claude Code — autonomous task completion with independent verification",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"postinstall": "node scripts/postinstall.js",
|
|
7
7
|
"uninstall": "node scripts/uninstall.js",
|
|
@@ -46,4 +46,4 @@
|
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=16"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -19,6 +19,14 @@ const runtimeSources = [
|
|
|
19
19
|
["src/model-upgrade-table.md", path.join(deskDir, "model-upgrade-table.md")],
|
|
20
20
|
["README.md", path.join(deskDir, "README.md")],
|
|
21
21
|
["install.sh", path.join(deskDir, "install.sh")],
|
|
22
|
+
// v0.14.0: zsh runner is the canonical --mode tmux backend again.
|
|
23
|
+
// src/node/run.mjs spawns it as a subprocess for tmux mode invocations.
|
|
24
|
+
// injectBannerAndLock preserves the shebang and adds a `# DO NOT EDIT`
|
|
25
|
+
// line on line 2 so the verification scripts in CLAUDE.md still
|
|
26
|
+
// recognize the file as installed.
|
|
27
|
+
["src/scripts/init_ralph_desk.zsh", path.join(deskDir, "init_ralph_desk.zsh")],
|
|
28
|
+
["src/scripts/run_ralph_desk.zsh", path.join(deskDir, "run_ralph_desk.zsh")],
|
|
29
|
+
["src/scripts/lib_ralph_desk.zsh", path.join(deskDir, "lib_ralph_desk.zsh")],
|
|
22
30
|
// v5.7 §4.15: all rlp-desk docs (user-facing + dev meta) under docs/rlp-desk/.
|
|
23
31
|
["docs/rlp-desk/architecture.md", path.join(docsDir, "rlp-desk", "architecture.md")],
|
|
24
32
|
["docs/rlp-desk/getting-started.md", path.join(docsDir, "rlp-desk", "getting-started.md")],
|
|
@@ -26,11 +34,12 @@ const runtimeSources = [
|
|
|
26
34
|
["docs/rlp-desk/TODO-verification-next.md", path.join(docsDir, "rlp-desk", "TODO-verification-next.md")],
|
|
27
35
|
["docs/rlp-desk/multi-mission-orchestration.md", path.join(docsDir, "rlp-desk", "multi-mission-orchestration.md")],
|
|
28
36
|
];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
// v0.14.0: legacy-deletion list cleared. The Node-canonical era (v5.7+)
|
|
38
|
+
// removed zsh after install; v0.14.0 reverts that — the zsh runner is the
|
|
39
|
+
// production --mode tmux path. Keep the empty array so the surrounding loop
|
|
40
|
+
// (`for (const legacyFile of legacyFiles)`) remains a no-op without churning
|
|
41
|
+
// the call site.
|
|
42
|
+
const legacyFiles = [];
|
|
34
43
|
|
|
35
44
|
function getNodeVersion() {
|
|
36
45
|
return process.env.RLP_DESK_NODE_VERSION_OVERRIDE || process.version;
|
package/scripts/uninstall.js
CHANGED
|
@@ -20,6 +20,11 @@ const files = [
|
|
|
20
20
|
path.join(deskDir, "model-upgrade-table.md"),
|
|
21
21
|
path.join(deskDir, "README.md"),
|
|
22
22
|
path.join(deskDir, "install.sh"),
|
|
23
|
+
// v0.14.0: zsh tmux runner is part of the install set again — clean it up
|
|
24
|
+
// on uninstall so users do not end up with orphaned 0o444 files.
|
|
25
|
+
path.join(deskDir, "init_ralph_desk.zsh"),
|
|
26
|
+
path.join(deskDir, "run_ralph_desk.zsh"),
|
|
27
|
+
path.join(deskDir, "lib_ralph_desk.zsh"),
|
|
23
28
|
];
|
|
24
29
|
|
|
25
30
|
for (const targetPath of files) {
|
package/src/commands/rlp-desk.md
CHANGED
|
@@ -278,9 +278,18 @@ Cross-project aggregation: scan `~/.claude/ralph-desk/analytics/` and read each
|
|
|
278
278
|
|
|
279
279
|
Parse the `--mode` flag. If absent or `agent`, use the Agent() path below. If `tmux`, use the Tmux path.
|
|
280
280
|
|
|
281
|
+
> **v0.14.0 stability tiers:**
|
|
282
|
+
> - `--mode tmux` is the **stable, production** path. The Node leader (`run.mjs`)
|
|
283
|
+
> now routes tmux invocations to `~/.claude/ralph-desk/run_ralph_desk.zsh`
|
|
284
|
+
> as a subprocess — that runner has the full safety net (heartbeat,
|
|
285
|
+
> copy-mode guard, prompt-stall, no-progress detection, claude model
|
|
286
|
+
> upgrade chain). Recommend this for autonomous campaigns.
|
|
287
|
+
> - `--mode agent` is **alpha** (Node-native LLM-driven Leader). The runner
|
|
288
|
+
> emits a stderr warning when this mode is invoked.
|
|
289
|
+
|
|
281
290
|
#### Tmux Mode (`--mode tmux`)
|
|
282
291
|
|
|
283
|
-
When `--mode tmux` is specified (v0.
|
|
292
|
+
When `--mode tmux` is specified (v0.14.0+: `run.mjs` accepts the same flags as before but spawns `run_ralph_desk.zsh` as a subprocess and inherits stdio. Flywheel and self-verification flags are not honored under tmux mode — they require `--mode agent`):
|
|
284
293
|
|
|
285
294
|
1. **Validate scaffold** — same as Agent() mode: check `.rlp-desk/prompts/<slug>.worker.prompt.md` etc.
|
|
286
295
|
2. **Check sentinels** — same as Agent() mode.
|
|
@@ -322,9 +331,8 @@ node ~/.claude/ralph-desk/node/run.mjs run '<slug>' \
|
|
|
322
331
|
- MUST launch with `run_in_background: true` so `/rlp-desk` returns control immediately while preserving live tmux visibility.
|
|
323
332
|
- Run-in-background is used so the shell can keep the command visible and keep the pane layout stable for status checks and completion flow.
|
|
324
333
|
- Do NOT kill panes after completion. Panes stay alive for inspection. User cleans up with `/rlp-desk clean <slug> --kill-session`.
|
|
325
|
-
- `--with-self-verification`
|
|
326
|
-
-
|
|
327
|
-
- Legacy `zsh ~/.claude/ralph-desk/run_ralph_desk.zsh` (deprecated in 0.12.0) still runs for non-flywheel/non-SV invocations but emits a deprecation `[notice]`. Calling it with `FLYWHEEL` or `WITH_SELF_VERIFICATION` env vars exits 2 with a migration banner pointing to the Node leader.
|
|
334
|
+
- v0.14.0: `--with-self-verification`, `--flywheel`, and `--flywheel-guard` are **not honored** under `--mode tmux` — the zsh runner has no SV/flywheel implementation. The Node leader emits a stderr WARNING listing the dropped flags. For SV/flywheel, use `--mode agent` (alpha).
|
|
335
|
+
- The slash command always invokes `node ~/.claude/ralph-desk/node/run.mjs run --mode tmux ...`. Do NOT invoke `~/.claude/ralph-desk/run_ralph_desk.zsh` directly — the Node router resolves the runner path, runs legacy detection, and surfaces actionable errors when the runner is missing.
|
|
328
336
|
|
|
329
337
|
**tmux UX model (5 items):**
|
|
330
338
|
- The session returns immediately after launch (`run_in_background: true`) so the command returns control to the parent CLI.
|
|
@@ -333,7 +341,13 @@ node ~/.claude/ralph-desk/node/run.mjs run '<slug>' \
|
|
|
333
341
|
- On completion, the command returns a completion notification before the loop ends.
|
|
334
342
|
- Agent mode remains unchanged, and no tmux-specific behavior is mixed into Agent mode.
|
|
335
343
|
|
|
336
|
-
#### Agent Mode (`--mode agent` or default)
|
|
344
|
+
#### Agent Mode (`--mode agent` or default — **alpha**)
|
|
345
|
+
|
|
346
|
+
> **v0.14.0:** Agent mode is the alpha LLM-driven path. The Node port shipped
|
|
347
|
+
> without zsh-equivalent safety nets (heartbeat, copy-mode guard, prompt-stall
|
|
348
|
+
> timeout, no-progress detection, claude model upgrade chain). The runner
|
|
349
|
+
> emits a stderr WARNING when agent mode is invoked. For production
|
|
350
|
+
> autonomous campaigns, prefer `--mode tmux`.
|
|
337
351
|
|
|
338
352
|
**Why Agent mode is structurally immune to Bug 4/5 (mid-execution prompt hang
|
|
339
353
|
& A4 premature dispatch):** Worker/Verifier are dispatched as `Agent(...,
|
package/src/node/run.mjs
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
1
3
|
import path from 'node:path';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
2
5
|
import { fileURLToPath } from 'node:url';
|
|
3
6
|
|
|
4
7
|
import { initCampaign } from './init/campaign-initializer.mjs';
|
|
5
8
|
import { readStatus } from './reporting/campaign-reporting.mjs';
|
|
6
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
run as runCampaignMain,
|
|
11
|
+
detectLegacyDeskInRunMode,
|
|
12
|
+
} from './runner/campaign-main-loop.mjs';
|
|
7
13
|
import { isClaudeEngine } from './cli/command-builder.mjs';
|
|
8
14
|
|
|
9
15
|
const RUN_DEFAULTS = {
|
|
@@ -211,6 +217,102 @@ async function runStatusCommand(args, deps) {
|
|
|
211
217
|
return 0;
|
|
212
218
|
}
|
|
213
219
|
|
|
220
|
+
// v0.14.0: Default location of the zsh runner installed by postinstall.js
|
|
221
|
+
// (Phase 3 of the v0.14.0 plan re-enables this sync). Overridable via
|
|
222
|
+
// RLP_DESK_ZSH_RUNNER for development checkouts that point to src/scripts.
|
|
223
|
+
function defaultZshRunnerPath() {
|
|
224
|
+
return (
|
|
225
|
+
process.env.RLP_DESK_ZSH_RUNNER
|
|
226
|
+
|| path.join(os.homedir(), '.claude', 'ralph-desk', 'run_ralph_desk.zsh')
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// v0.14.0: convert parsed CLI options to env vars consumed by run_ralph_desk.zsh.
|
|
231
|
+
// Names mirror the variables declared in src/scripts/run_ralph_desk.zsh
|
|
232
|
+
// (LOOP_NAME, ROOT, WORKER_MODEL, VERIFIER_MODEL, FINAL_VERIFIER_MODEL,
|
|
233
|
+
// MAX_ITER, ITER_TIMEOUT, CB_THRESHOLD, VERIFY_MODE, CONSENSUS_MODE,
|
|
234
|
+
// CONSENSUS_MODEL, FINAL_CONSENSUS_MODEL, LOCK_WORKER_MODEL, AUTONOMOUS_MODE,
|
|
235
|
+
// LANE_MODE, TEST_DENSITY_MODE).
|
|
236
|
+
function buildZshEnv(slug, options, parentEnv) {
|
|
237
|
+
return {
|
|
238
|
+
...parentEnv,
|
|
239
|
+
LOOP_NAME: slug,
|
|
240
|
+
ROOT: options.rootDir,
|
|
241
|
+
WORKER_MODEL: options.workerModel,
|
|
242
|
+
VERIFIER_MODEL: options.verifierModel,
|
|
243
|
+
FINAL_VERIFIER_MODEL: options.finalVerifierModel,
|
|
244
|
+
MAX_ITER: String(options.maxIterations),
|
|
245
|
+
ITER_TIMEOUT: String(options.iterTimeout),
|
|
246
|
+
CB_THRESHOLD: String(options.cbThreshold),
|
|
247
|
+
VERIFY_MODE: options.verifyMode,
|
|
248
|
+
CONSENSUS_MODE: options.consensusMode,
|
|
249
|
+
CONSENSUS_MODEL: options.consensusModel,
|
|
250
|
+
FINAL_CONSENSUS_MODEL: options.finalConsensusModel,
|
|
251
|
+
LOCK_WORKER_MODEL: options.lockWorkerModel ? '1' : '0',
|
|
252
|
+
AUTONOMOUS_MODE: options.autonomous ? '1' : '0',
|
|
253
|
+
LANE_MODE: options.laneStrict ? 'strict' : 'warn',
|
|
254
|
+
TEST_DENSITY_MODE: options.testDensityStrict ? 'strict' : 'warn',
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// v0.14.0: default tmux-mode delegate. Spawns the zsh runner inheriting stdio
|
|
259
|
+
// so the operator sees pane orchestration in real time. Resolves with the
|
|
260
|
+
// child exit code (or 1 on spawn error) to keep the caller deterministic.
|
|
261
|
+
function defaultSpawnZsh(zshPath, env, cwd) {
|
|
262
|
+
return new Promise((resolve) => {
|
|
263
|
+
const child = spawn('zsh', [zshPath], { env, stdio: 'inherit', cwd });
|
|
264
|
+
child.on('error', (err) => {
|
|
265
|
+
process.stderr.write(`failed to spawn zsh runner: ${err.message}\n`);
|
|
266
|
+
resolve(1);
|
|
267
|
+
});
|
|
268
|
+
child.on('exit', (code, signal) => {
|
|
269
|
+
if (signal) {
|
|
270
|
+
resolve(128 + (typeof signal === 'string' ? 0 : signal));
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
resolve(typeof code === 'number' ? code : 0);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function runTmuxViaZsh(slug, options, deps) {
|
|
279
|
+
// v0.13.0 legacy detection still applies — the zsh runner shares the same
|
|
280
|
+
// .rlp-desk/ contract, so a stray .claude/ralph-desk/ left over from an
|
|
281
|
+
// older campaign must be migrated by the operator before we hand off.
|
|
282
|
+
const legacy = detectLegacyDeskInRunMode(options.rootDir, process.env);
|
|
283
|
+
if (legacy) {
|
|
284
|
+
write(deps.stderr, legacy.message);
|
|
285
|
+
return 1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const zshPath = (deps.zshRunnerPath ?? defaultZshRunnerPath)();
|
|
289
|
+
if (!deps.fileExists(zshPath)) {
|
|
290
|
+
write(
|
|
291
|
+
deps.stderr,
|
|
292
|
+
`ERROR: zsh runner not found at ${zshPath}. Run \`npm install rlp-desk\` (or set RLP_DESK_ZSH_RUNNER) to sync.`,
|
|
293
|
+
);
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Surface flags the zsh runner cannot honor. Flywheel and self-verification
|
|
298
|
+
// remain Node-leader features, available only in --mode agent. Warn loudly
|
|
299
|
+
// instead of silent no-op so the operator understands the trade-off.
|
|
300
|
+
const unsupported = [];
|
|
301
|
+
if (options.flywheel !== 'off') unsupported.push('--flywheel');
|
|
302
|
+
if (options.flywheelGuard !== 'off') unsupported.push('--flywheel-guard');
|
|
303
|
+
if (options.withSelfVerification) unsupported.push('--with-self-verification');
|
|
304
|
+
if (unsupported.length > 0) {
|
|
305
|
+
write(
|
|
306
|
+
deps.stderr,
|
|
307
|
+
`WARNING: ${unsupported.join(', ')} not honored in --mode tmux (zsh runner). Use --mode agent for those features.`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const env = buildZshEnv(slug, options, process.env);
|
|
312
|
+
const spawnZsh = deps.spawnZsh ?? defaultSpawnZsh;
|
|
313
|
+
return spawnZsh(zshPath, env, options.rootDir);
|
|
314
|
+
}
|
|
315
|
+
|
|
214
316
|
async function runRunCommand(args, deps) {
|
|
215
317
|
if (args.length === 0) {
|
|
216
318
|
throw new Error('run requires a slug');
|
|
@@ -250,6 +352,27 @@ async function runRunCommand(args, deps) {
|
|
|
250
352
|
);
|
|
251
353
|
}
|
|
252
354
|
|
|
355
|
+
// v0.14.0: --mode tmux delegates to the zsh runner. Node leader keeps
|
|
356
|
+
// ownership of --mode agent only (LLM-driven orchestration).
|
|
357
|
+
if (options.mode === 'tmux') {
|
|
358
|
+
return runTmuxViaZsh(slug, options, deps);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// v0.14.0: agent mode is the alpha LLM-driven path. The Node port shipped
|
|
362
|
+
// without zsh-equivalent safety nets (heartbeat, copy-mode guard,
|
|
363
|
+
// prompt-stall timeout, no-progress detection, claude model upgrade chain).
|
|
364
|
+
// Surface that explicitly so production users pick --mode tmux instead.
|
|
365
|
+
if (
|
|
366
|
+
options.mode === 'agent'
|
|
367
|
+
&& !process.env.RLP_DESK_QUIET_WARNINGS
|
|
368
|
+
&& process.env.NODE_ENV !== 'test'
|
|
369
|
+
) {
|
|
370
|
+
write(
|
|
371
|
+
deps.stderr,
|
|
372
|
+
'WARNING: --mode agent is alpha. For production tmux orchestration, prefer --mode tmux (zsh-backed, stable).',
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
253
376
|
const result = await deps.runCampaign(slug, options);
|
|
254
377
|
// governance §1f BLOCKED Surfacing: surface the blocked reason on stderr so
|
|
255
378
|
// the operator (or wrapper script) does not have to grep memo files.
|
|
@@ -273,6 +396,13 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
273
396
|
initCampaign: overrides.initCampaign ?? initCampaign,
|
|
274
397
|
readStatus: overrides.readStatus ?? readStatus,
|
|
275
398
|
runCampaign: overrides.runCampaign ?? runCampaignMain,
|
|
399
|
+
// v0.14.0: --mode tmux delegate. Tests inject `spawnZsh` to assert the
|
|
400
|
+
// env mapping without actually fork+exec'ing zsh. `fileExists` and
|
|
401
|
+
// `zshRunnerPath` are similarly injectable so a test can pretend the
|
|
402
|
+
// installed runner is or isn't present.
|
|
403
|
+
spawnZsh: overrides.spawnZsh,
|
|
404
|
+
zshRunnerPath: overrides.zshRunnerPath,
|
|
405
|
+
fileExists: overrides.fileExists ?? ((p) => fs.existsSync(p)),
|
|
276
406
|
};
|
|
277
407
|
|
|
278
408
|
try {
|
|
@@ -949,6 +949,12 @@ export function shouldRunGuard(flywheelGuard, state, usId) {
|
|
|
949
949
|
return true;
|
|
950
950
|
}
|
|
951
951
|
|
|
952
|
+
// v0.14.0: production --mode tmux is routed to the zsh runner by
|
|
953
|
+
// src/node/run.mjs (see runTmuxViaZsh). The Node leader below owns the
|
|
954
|
+
// --mode agent (LLM-driven) flow. In-tree tests still exercise this path
|
|
955
|
+
// with `mode: 'tmux'` as a label while injecting fake
|
|
956
|
+
// createSession/sendKeys/pollForSignal — that is intentional and is NOT a
|
|
957
|
+
// regression of the routing contract.
|
|
952
958
|
export async function run(slug, options = {}) {
|
|
953
959
|
const rootDir = path.resolve(options.rootDir ?? process.cwd());
|
|
954
960
|
const env = options.env ?? process.env;
|