@ai-dev-methodologies/rlp-desk 0.15.4 → 0.15.6
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/CHANGELOG.md +33 -1
- package/README.md +43 -40
- package/docs/rlp-desk/getting-started.md +6 -3
- package/package.json +9 -3
- package/src/node/MANIFEST.txt +3 -0
- package/src/node/cli/command-builder.mjs +5 -2
- package/src/node/prompts/prompt-assembler.mjs +2 -2
- package/src/node/run.mjs +70 -3
- package/src/node/runner/campaign-main-loop.mjs +13 -2
- package/src/scripts/run_ralph_desk.zsh +5 -3
- package/docs/rlp-desk/internal/verification-policy-gap-analysis.md +0 -523
- package/docs/rlp-desk/internal/verification-strategy-research.md +0 -2097
- package/docs/rlp-desk/plans/cozy-gliding-trinket.md +0 -53
- package/docs/rlp-desk/plans/frolicking-churning-honey.md +0 -253
- package/docs/rlp-desk/plans/keen-sauteeing-snowflake.md +0 -245
- package/docs/rlp-desk/plans/mutable-booping-corbato.md +0 -163
- package/docs/rlp-desk/plans/rlp-desk-0.11-handoff-7fixes.md +0 -352
- package/docs/rlp-desk/plans/rlp-desk-0.11.1-tmux-pane-disappearance.md +0 -260
- package/docs/rlp-desk/plans/rlp-desk-elegant-papert-agent-a8cd695ffca2a3ad8.md +0 -84
- package/docs/rlp-desk/plans/rlp-desk-elegant-papert.md +0 -270
- package/docs/rlp-desk/plans/rlp-desk-tmux-flywheel-routing.md +0 -730
- package/docs/rlp-desk/plans/toasty-whistling-diffie-agent-a6814625642e956da.md +0 -201
- package/docs/rlp-desk/plans/toasty-whistling-diffie.md +0 -117
- package/docs/rlp-desk/plans/validated-snacking-crayon.md +0 -204
- package/examples/calculator/.claude/ralph-desk/logs/loop-test/iter-001.worker-output.log +0 -0
- package/examples/calculator/.claude/ralph-desk/logs/loop-test/iter-001.worker-prompt.md +0 -38
- package/examples/calculator/.claude/ralph-desk/logs/loop-test/iter-001.worker-trigger.sh +0 -28
- package/examples/calculator/.claude/ralph-desk/logs/loop-test/session-config.json +0 -25
- package/examples/calculator/.claude/ralph-desk/logs/loop-test/status.json +0 -10
- package/examples/calculator/.claude/ralph-desk/logs/loop-test/worker-heartbeat.json +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,39 @@ For pre-v0.15.4 versions, refer to `git log` and individual GitHub release notes
|
|
|
11
11
|
- Post-v0.15.6: remove `RLP_LIFECYCLE_METRICS` flag entirely (per plan v3 ADR follow-ups).
|
|
12
12
|
- Phase D.1 (handoff documents) + Phase D.2 (per-stage agent role specialization) — both deferred per `docs/plans/v0.15.4-release-runbook.md` §7.6.
|
|
13
13
|
|
|
14
|
-
## [0.15.
|
|
14
|
+
## [0.15.6] — 2026-06-18
|
|
15
|
+
|
|
16
|
+
Patch: CI/test integrity, a codex command-builder security fix, and documentation reconciliation.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Codex worker command no longer passes model/reasoning unquoted.** `buildCodexCmd` now shell-quotes the model and reasoning values (parity with the claude path), closing a shell-injection / argument-splitting hazard when operator-supplied flags reach the shell via tmux send-keys.
|
|
20
|
+
- **Docs now describe the correct execution modes.** The README "execution modes" section conflated the slash-command default (`--mode native`, `Agent()`-based) with the deprecated `--mode agent` (Node CLI). It is rewritten to the accurate three-mode model: `--mode tmux` is the canonical/recommended path, `--mode native` is the default companion for short/interactive use, `--mode agent` is deprecated.
|
|
21
|
+
- **Docs now point to the correct scaffold path.** Getting-started and README referenced the pre-v0.13.0 `.claude/ralph-desk/` project-local path; corrected to `.rlp-desk/` (the global install path `~/.claude/ralph-desk/` is unchanged).
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- **CI now runs the behavioral test suites.** A new CI job runs `test:node` + `test:zsh` (previously CI ran only the existence-grep fast gate). First rollout is non-blocking to inventory CI-only flakiness before being made blocking.
|
|
25
|
+
- **The full SV gate verifies the source tree.** `sv-gate:full`'s real-campaign E2E now targets the in-repo `src/` leader (and the correct `.rlp-desk/` sentinel paths) instead of the installed copy, so the gate validates the code being merged.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- **ADR-001 (Leader Consolidation).** Records the decision to make `--mode tmux` the canonical production leader, deprecate the `--mode agent` Node-CLI entry point on a dated schedule, and retain `--mode native` as a second-class companion. (Internal; not shipped in the tarball.)
|
|
29
|
+
|
|
30
|
+
## [0.15.5] — 2026-06-17
|
|
31
|
+
|
|
32
|
+
Patch: fixes surfaced by a fresh-context live dogfood of the tmux and agent run modes, plus packaging hygiene.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **Curl-install (`install.sh`) produced a Node leader that could not start.** `src/node/MANIFEST.txt` was missing three runtime modules, so a curl-installed `--mode agent` / Node leader threw `ERR_MODULE_NOT_FOUND` at startup. MANIFEST regenerated. (npm installs were unaffected.)
|
|
36
|
+
- **Tmux-mode leader could hang at startup.** A bare `> "$COST_LOG"` redirect runs zsh's `$NULLCMD` (`cat`), which blocks reading stdin when launched with an open TTY (interactive shell / tmux pane). Changed to `: > "$COST_LOG"`.
|
|
37
|
+
- **Worker sometimes printed the iteration signal instead of writing it.** The per-iteration prompt now explicitly instructs the worker to WRITE the verify signal to the iter-signal file (resolved path in tmux mode).
|
|
38
|
+
- **Clearer error for mis-leveled PRD user-story headings.** When user stories use `###` instead of `## US-NNN` (H2), the Node leader now hints at the correct heading level instead of a bare "No user stories found".
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- **`clean <slug> [--kill-session]` for the Node CLI.** Resets a campaign — removes sentinels, signal/claim/verdict files, and runtime state, while preserving the PRD, test-spec, prompts, memory, and reports. Previously unimplemented in the Node rewrite, which left a blocked campaign with no recovery path.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
- **Smaller published package.** The tarball no longer ships internal research docs, dev planning handoffs, or example runtime state (73 → 53 files, ~364 → ~244 kB).
|
|
45
|
+
|
|
46
|
+
## [0.15.4] — 2026-05-08
|
|
15
47
|
|
|
16
48
|
Phase B: tmux/process lifecycle hardening + observability + real-LLM SV strengthening. 4 sequential PRs (B1, B2-FIX, B4, B3) merged to main, plus pre-release audit fix branch addressing 16 findings (3 CRITICAL, 6 HIGH, 5 MEDIUM, 2 LOW).
|
|
17
49
|
|
package/README.md
CHANGED
|
@@ -275,45 +275,48 @@ The brainstorm phase evaluates complexity (US count, file scope, logic, dependen
|
|
|
275
275
|
|
|
276
276
|
## Execution Modes
|
|
277
277
|
|
|
278
|
-
RLP Desk
|
|
279
|
-
|
|
280
|
-
> **
|
|
281
|
-
>
|
|
282
|
-
> no-progress detection,
|
|
283
|
-
> and
|
|
284
|
-
>
|
|
285
|
-
> use
|
|
278
|
+
RLP Desk has three execution modes, all honoring the same governance protocol. **`--mode tmux` is the canonical, recommended path for any real campaign** (see [ADR-001](docs/plans/adr-001-leader-consolidation.md)).
|
|
279
|
+
|
|
280
|
+
> **Mode status:**
|
|
281
|
+
> - **`--mode tmux`** (zsh-backed) — **stable / production / canonical.** Full safety net (heartbeat,
|
|
282
|
+
> copy-mode guard, prompt-stall timeout, no-progress detection, model upgrade chain). Use this for
|
|
283
|
+
> long campaigns and autonomous loops.
|
|
284
|
+
> - **`--mode native`** (the slash-command **default**) — the current Claude Code session is the Leader,
|
|
285
|
+
> dispatching via `Agent()`. Works anywhere (no tmux), good for short/interactive use, but is a
|
|
286
|
+
> second-class companion: no iteration watchdog, turn-based pauses possible. Not for long unattended runs.
|
|
287
|
+
> - **`--mode agent`** (direct Node CLI) — **deprecated alpha**, on a removal schedule (ADR-001). Prints a
|
|
288
|
+
> SCHEDULED-REMOVAL banner. Do not use for new work; prefer `--mode tmux`.
|
|
286
289
|
|
|
287
290
|
### Environment Compatibility
|
|
288
291
|
|
|
289
|
-
| Environment |
|
|
290
|
-
|
|
291
|
-
| Claude Code (any terminal) | **Works** | Requires tmux |
|
|
292
|
-
| Inside tmux session | **Works** | **Works** — panes split in current window |
|
|
293
|
-
| Outside tmux session | **Works** | **Rejected** — "start tmux first" |
|
|
292
|
+
| Environment | Native (default) | Tmux (canonical) | Agent (deprecated) |
|
|
293
|
+
|-------------|------------------|------------------|--------------------|
|
|
294
|
+
| Claude Code (any terminal) | **Works** | Requires tmux | Works |
|
|
295
|
+
| Inside tmux session | **Works** | **Works** — panes split in current window | Works |
|
|
296
|
+
| Outside tmux session | **Works** | **Rejected** — "start tmux first" | Works |
|
|
294
297
|
|
|
295
298
|
### Choosing Your Mode
|
|
296
299
|
|
|
297
300
|
| Need | Use |
|
|
298
301
|
|------|-----|
|
|
299
|
-
| Production / autonomous campaigns | `--mode tmux` (
|
|
300
|
-
|
|
|
301
|
-
|
|
|
302
|
+
| Production / autonomous / overnight / CI campaigns | `--mode tmux` (canonical) |
|
|
303
|
+
| Quick interactive exploration, no tmux available | `--mode native` (default) |
|
|
304
|
+
| (legacy direct-Node-CLI workflows) | `--mode agent` — deprecated; migrate to `--mode tmux` |
|
|
302
305
|
|
|
303
|
-
###
|
|
306
|
+
### Native Mode (slash-command default) — "Smart Mode"
|
|
304
307
|
|
|
305
308
|
```
|
|
306
|
-
/rlp-desk run calculator
|
|
309
|
+
/rlp-desk run calculator # defaults to --mode native
|
|
307
310
|
```
|
|
308
311
|
|
|
309
312
|
The current Claude Code session acts as the Leader, dispatching Workers and Verifiers via `Agent()`. The Leader is an LLM that dynamically routes models and reasons about context.
|
|
310
313
|
|
|
311
314
|
- Works anywhere — no tmux required
|
|
312
315
|
- Dynamic model routing — Leader upgrades models on failure
|
|
313
|
-
|
|
314
|
-
**Known limitation:** Agent mode runs inside Claude Code's turn-based request-response model. If the LLM outputs text without a tool call, the turn terminates and the loop pauses until the user sends "continue." This is a platform constraint — the protocol mitigates it but cannot guarantee 100% uninterrupted execution. For guaranteed autonomous loops, use tmux mode.
|
|
315
316
|
- Fix Loop — extracts verifier issues and feeds them back to the next worker
|
|
316
|
-
- Best for interactive development
|
|
317
|
+
- Best for short, interactive development
|
|
318
|
+
|
|
319
|
+
**Known limitation:** Native mode runs inside Claude Code's turn-based request-response model. If the LLM outputs text without a tool call, the turn terminates and the loop pauses until the user sends "continue." This is a platform constraint — the protocol mitigates it but cannot guarantee 100% uninterrupted execution. **For guaranteed autonomous loops, use `--mode tmux`.**
|
|
317
320
|
|
|
318
321
|
### Tmux Mode — "Lean Mode"
|
|
319
322
|
|
|
@@ -456,7 +459,7 @@ Each conflict is logged as a JSONL entry in `logs/<slug>/conflict-log.jsonl`:
|
|
|
456
459
|
After the campaign, review the conflict log to identify systemic issues:
|
|
457
460
|
|
|
458
461
|
```bash
|
|
459
|
-
cat .
|
|
462
|
+
cat .rlp-desk/logs/<slug>/conflict-log.jsonl | jq .
|
|
460
463
|
```
|
|
461
464
|
|
|
462
465
|
Common patterns:
|
|
@@ -471,20 +474,20 @@ After `init`, your project gets this scaffold:
|
|
|
471
474
|
```
|
|
472
475
|
your-project/
|
|
473
476
|
├── .claude/
|
|
474
|
-
│
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
│
|
|
478
|
-
│
|
|
479
|
-
|
|
480
|
-
│
|
|
481
|
-
|
|
482
|
-
│
|
|
483
|
-
|
|
484
|
-
│
|
|
485
|
-
│
|
|
486
|
-
|
|
487
|
-
|
|
477
|
+
│ └── settings.local.json # rlp-desk permissions (auto-added by init)
|
|
478
|
+
└── .rlp-desk/ # scaffold (v0.13.0+; was .claude/ralph-desk/)
|
|
479
|
+
├── prompts/
|
|
480
|
+
│ ├── <slug>.worker.prompt.md
|
|
481
|
+
│ └── <slug>.verifier.prompt.md
|
|
482
|
+
├── context/
|
|
483
|
+
│ └── <slug>-latest.md
|
|
484
|
+
├── memos/
|
|
485
|
+
│ └── <slug>-memory.md
|
|
486
|
+
├── plans/
|
|
487
|
+
│ ├── prd-<slug>.md
|
|
488
|
+
│ └── test-spec-<slug>.md
|
|
489
|
+
└── logs/<slug>/
|
|
490
|
+
└── status.json
|
|
488
491
|
```
|
|
489
492
|
|
|
490
493
|
### Local Settings
|
|
@@ -495,15 +498,15 @@ your-project/
|
|
|
495
498
|
{
|
|
496
499
|
"permissions": {
|
|
497
500
|
"allow": [
|
|
498
|
-
"Read(.
|
|
499
|
-
"Edit(.
|
|
500
|
-
"Write(.
|
|
501
|
+
"Read(.rlp-desk/**)",
|
|
502
|
+
"Edit(.rlp-desk/**)",
|
|
503
|
+
"Write(.rlp-desk/**)"
|
|
501
504
|
]
|
|
502
505
|
}
|
|
503
506
|
}
|
|
504
507
|
```
|
|
505
508
|
|
|
506
|
-
**Why:**
|
|
509
|
+
**Why:** Since v0.13.0 the scaffold lives at `.rlp-desk/` (outside `.claude/`), so Claude Code's `.claude/` sensitive-file gate no longer blocks Worker/Verifier writes. These explicit `.rlp-desk/**` permissions are a belt-and-suspenders helper that keeps automated loop execution prompt-free.
|
|
507
510
|
|
|
508
511
|
**Note:** `settings.local.json` is local to your machine and is not committed to git. If the file already exists, permissions are merged without overwriting your existing settings.
|
|
509
512
|
|
|
@@ -64,7 +64,7 @@ On approval, brainstorm offers to run `init` automatically.
|
|
|
64
64
|
This creates the scaffold:
|
|
65
65
|
|
|
66
66
|
```
|
|
67
|
-
.
|
|
67
|
+
.rlp-desk/
|
|
68
68
|
├── prompts/
|
|
69
69
|
│ ├── loop-test.worker.prompt.md
|
|
70
70
|
│ └── loop-test.verifier.prompt.md
|
|
@@ -78,9 +78,12 @@ This creates the scaffold:
|
|
|
78
78
|
└── logs/loop-test/
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
> Since v0.13.0 the scaffold lives at the project-local `.rlp-desk/` (not `.claude/ralph-desk/`),
|
|
82
|
+
> so Claude Code's `.claude/` sensitive-file gate no longer blocks Worker/Verifier writes.
|
|
83
|
+
|
|
81
84
|
## Step 5: Customize the PRD
|
|
82
85
|
|
|
83
|
-
Edit `.
|
|
86
|
+
Edit `.rlp-desk/plans/prd-loop-test.md` to define your user stories and acceptance criteria. See [`examples/calculator/`](../examples/calculator/.claude/ralph-desk/plans/prd-loop-test.md) for a complete example.
|
|
84
87
|
|
|
85
88
|
Key sections:
|
|
86
89
|
- **User Stories** with Given/When/Then acceptance criteria, Task Type, and Risk Level
|
|
@@ -91,7 +94,7 @@ Key sections:
|
|
|
91
94
|
|
|
92
95
|
## Step 6: Define the Test Spec
|
|
93
96
|
|
|
94
|
-
Edit `.
|
|
97
|
+
Edit `.rlp-desk/plans/test-spec-loop-test.md` to specify verification commands:
|
|
95
98
|
|
|
96
99
|
```markdown
|
|
97
100
|
## Verification Commands
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-dev-methodologies/rlp-desk",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.6",
|
|
4
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",
|
|
8
|
+
"manifest:check": "node scripts/build-node-manifest.js --check",
|
|
9
|
+
"prepublishOnly": "node scripts/build-node-manifest.js --check",
|
|
8
10
|
"test:node": "node --test 'tests/node/*.mjs' 'tests/node/*.test.mjs'",
|
|
9
11
|
"test:zsh": "for f in tests/test_*.sh; do echo \"=== $f ===\"; zsh \"$f\" || exit 1; done",
|
|
10
12
|
"test:fast": "npm run test:node",
|
|
@@ -19,8 +21,12 @@
|
|
|
19
21
|
"src/governance.md",
|
|
20
22
|
"src/model-upgrade-table.md",
|
|
21
23
|
"scripts/",
|
|
22
|
-
"docs/rlp-desk
|
|
23
|
-
"
|
|
24
|
+
"docs/rlp-desk/*.md",
|
|
25
|
+
"docs/rlp-desk/blueprints/",
|
|
26
|
+
"examples/calculator/.claude/ralph-desk/context/",
|
|
27
|
+
"examples/calculator/.claude/ralph-desk/memos/",
|
|
28
|
+
"examples/calculator/.claude/ralph-desk/plans/",
|
|
29
|
+
"examples/calculator/.claude/ralph-desk/prompts/",
|
|
24
30
|
"install.sh",
|
|
25
31
|
"README.md",
|
|
26
32
|
"CHANGELOG.md",
|
package/src/node/MANIFEST.txt
CHANGED
|
@@ -7,9 +7,12 @@ reporting/campaign-reporting.mjs
|
|
|
7
7
|
run.mjs
|
|
8
8
|
runner/campaign-main-loop.mjs
|
|
9
9
|
runner/leader-registry.mjs
|
|
10
|
+
runner/prompt-detector.mjs
|
|
10
11
|
runner/prompt-dismisser.mjs
|
|
11
12
|
shared/fs.mjs
|
|
12
13
|
shared/paths.mjs
|
|
13
14
|
tmux/pane-manager.mjs
|
|
14
15
|
util/debug-log.mjs
|
|
16
|
+
util/desk-root.mjs
|
|
17
|
+
util/lifecycle-metrics.mjs
|
|
15
18
|
util/shell-quote.mjs
|
|
@@ -72,14 +72,17 @@ export function buildClaudeCmd(mode, model, options = {}) {
|
|
|
72
72
|
export function buildCodexCmd(mode, model, options = {}) {
|
|
73
73
|
assertTuiMode(mode, 'buildCodexCmd');
|
|
74
74
|
|
|
75
|
+
// GAP-2 (audit): shell-quote model + reasoning for parity with buildClaudeCmd.
|
|
76
|
+
// The command string is delivered to a shell (tmux send-keys), so unquoted
|
|
77
|
+
// operator-supplied values were a shell-injection / arg-splitting hazard.
|
|
75
78
|
const parts = [
|
|
76
79
|
CODEX_BIN,
|
|
77
80
|
'-m',
|
|
78
|
-
model,
|
|
81
|
+
shellQuote(model),
|
|
79
82
|
];
|
|
80
83
|
|
|
81
84
|
if (options.reasoning !== undefined) {
|
|
82
|
-
parts.push('-c', `model_reasoning_effort="${options.reasoning}"`);
|
|
85
|
+
parts.push('-c', shellQuote(`model_reasoning_effort="${options.reasoning}"`));
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
parts.push('--disable', 'plugins', '--dangerously-bypass-approvals-and-sandbox');
|
|
@@ -150,8 +150,8 @@ export async function assembleWorkerPrompt({
|
|
|
150
150
|
} else {
|
|
151
151
|
promptLines.push(`- **Test Spec**: Read \`${fullTestSpecPath}\` (full — find ${nextUs} section)`);
|
|
152
152
|
}
|
|
153
|
-
promptLines.push(`When done,
|
|
154
|
-
promptLines.push(`
|
|
153
|
+
promptLines.push(`When done, you MUST WRITE (not just print) the verify signal to the iter-signal FILE — Path: \`memos/<slug>-iter-signal.json\` (see the MANDATORY signal-file instruction in the base prompt).`);
|
|
154
|
+
promptLines.push(`Write this exact JSON to that file (us_id="${nextUs}", not "ALL"): {"iteration": N, "status": "verify", "us_id": "${nextUs}", "summary": "what was done", "timestamp": "ISO"}`);
|
|
155
155
|
promptLines.push('');
|
|
156
156
|
promptLines.push(`**Update the campaign memory's 'Next Iteration Contract' to reflect ${nextUs}.**`);
|
|
157
157
|
} else if (verifiedUs.length > 0) {
|
package/src/node/run.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { spawn } from 'node:child_process';
|
|
4
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
|
|
7
7
|
import { initCampaign } from './init/campaign-initializer.mjs';
|
|
@@ -9,6 +9,7 @@ import { readStatus } from './reporting/campaign-reporting.mjs';
|
|
|
9
9
|
import {
|
|
10
10
|
run as runCampaignMain,
|
|
11
11
|
detectLegacyDeskInRunMode,
|
|
12
|
+
buildPaths,
|
|
12
13
|
} from './runner/campaign-main-loop.mjs';
|
|
13
14
|
import { isClaudeEngine } from './cli/command-builder.mjs';
|
|
14
15
|
|
|
@@ -51,7 +52,7 @@ function buildHelpText() {
|
|
|
51
52
|
' run <slug> [options] Run loop (tmux=zsh leader [production], agent=Node leader [deprecated alpha], native=slash-only error)',
|
|
52
53
|
' status <slug> Show loop status',
|
|
53
54
|
' logs <slug> [N] Show iteration log (not implemented in the Node rewrite yet)',
|
|
54
|
-
' clean <slug> [--kill-session] Reset for re-run (
|
|
55
|
+
' clean <slug> [--kill-session] Reset for re-run (removes sentinels + runtime/; preserves PRD/prompts/memory)',
|
|
55
56
|
' resume <slug> Resume loop (not implemented in the Node rewrite yet)',
|
|
56
57
|
'',
|
|
57
58
|
'Run Options:',
|
|
@@ -217,6 +218,71 @@ async function runStatusCommand(args, deps) {
|
|
|
217
218
|
return 0;
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
// D-6 (dogfood): real `clean` for the Node leader. Previously "not implemented",
|
|
222
|
+
// which left a blocked campaign with NO recovery path (a transient parse error
|
|
223
|
+
// wrote a blocked sentinel that bricked re-runs). Removes the transient/terminal
|
|
224
|
+
// state (sentinels, signal/claim/verdict, runtime/) while PRESERVING the durable
|
|
225
|
+
// inputs (PRD, test-spec, prompts, context, memory) and the campaign report.
|
|
226
|
+
async function runCleanCommand(args, deps) {
|
|
227
|
+
if (args.length === 0 || args[0] === '--help') {
|
|
228
|
+
write(deps.stdout, 'Usage: node src/node/run.mjs clean <slug> [--kill-session]');
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
const slug = args[0];
|
|
232
|
+
const killSession = args.includes('--kill-session');
|
|
233
|
+
const paths = buildPaths(deps.cwd, slug);
|
|
234
|
+
|
|
235
|
+
// --kill-session: read the session name from runtime/session-config.json
|
|
236
|
+
// BEFORE removing runtime, then best-effort tmux teardown.
|
|
237
|
+
if (killSession) {
|
|
238
|
+
try {
|
|
239
|
+
const cfgPath = path.join(paths.runtimeDir, 'session-config.json');
|
|
240
|
+
if (deps.fileExists(cfgPath)) {
|
|
241
|
+
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
242
|
+
if (cfg && cfg.session_name) {
|
|
243
|
+
spawnSync('tmux', ['kill-session', '-t', cfg.session_name], { stdio: 'ignore' });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} catch { /* best-effort */ }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const transient = [
|
|
250
|
+
paths.blockedSentinel,
|
|
251
|
+
paths.blockedSentinel.replace(/\.md$/, '.json'),
|
|
252
|
+
paths.completeSentinel,
|
|
253
|
+
paths.completeSentinel.replace(/\.md$/, '.json'),
|
|
254
|
+
paths.signalFile,
|
|
255
|
+
paths.doneClaimFile,
|
|
256
|
+
paths.verdictFile,
|
|
257
|
+
paths.flywheelSignalFile,
|
|
258
|
+
paths.flywheelGuardVerdictFile,
|
|
259
|
+
];
|
|
260
|
+
let removed = 0;
|
|
261
|
+
for (const target of transient) {
|
|
262
|
+
try {
|
|
263
|
+
if (fs.existsSync(target)) {
|
|
264
|
+
// Sentinels may be chmod 0o444 (write-lock); relax before unlink.
|
|
265
|
+
try { fs.chmodSync(target, 0o644); } catch { /* ignore */ }
|
|
266
|
+
fs.rmSync(target, { force: true });
|
|
267
|
+
removed += 1;
|
|
268
|
+
}
|
|
269
|
+
} catch { /* best-effort per-file */ }
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
if (fs.existsSync(paths.runtimeDir)) {
|
|
273
|
+
fs.rmSync(paths.runtimeDir, { recursive: true, force: true });
|
|
274
|
+
removed += 1;
|
|
275
|
+
}
|
|
276
|
+
} catch { /* best-effort */ }
|
|
277
|
+
|
|
278
|
+
write(
|
|
279
|
+
deps.stdout,
|
|
280
|
+
`Cleaned ${slug}: removed ${removed} transient artifact(s) (sentinels, signal/claim/verdict, runtime/`
|
|
281
|
+
+ `${killSession ? ', tmux session' : ''}). Preserved PRD, test-spec, prompts, context, memory, reports.`,
|
|
282
|
+
);
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
|
|
220
286
|
// v0.14.0: Default location of the zsh runner installed by postinstall.js
|
|
221
287
|
// (Phase 3 of the v0.14.0 plan re-enables this sync). Overridable via
|
|
222
288
|
// RLP_DESK_ZSH_RUNNER for development checkouts that point to src/scripts.
|
|
@@ -466,9 +532,10 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
466
532
|
return await runRunCommand(rest, deps);
|
|
467
533
|
case 'status':
|
|
468
534
|
return await runStatusCommand(rest, deps);
|
|
535
|
+
case 'clean':
|
|
536
|
+
return await runCleanCommand(rest, deps);
|
|
469
537
|
case 'brainstorm':
|
|
470
538
|
case 'logs':
|
|
471
|
-
case 'clean':
|
|
472
539
|
case 'resume':
|
|
473
540
|
throw new Error(`${command} is not implemented in the Node rewrite yet`);
|
|
474
541
|
default:
|
|
@@ -93,7 +93,7 @@ export function detectLegacyDeskInRunMode(rootDir, env = process.env) {
|
|
|
93
93
|
return { legacyPath, newPath, message };
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
function buildPaths(rootDir, slug, env = process.env) {
|
|
96
|
+
export function buildPaths(rootDir, slug, env = process.env) {
|
|
97
97
|
const deskRoot = resolveDeskRoot(rootDir, env);
|
|
98
98
|
const campaignLogDir = path.join(deskRoot, 'logs', slug);
|
|
99
99
|
|
|
@@ -1462,7 +1462,18 @@ async function _runCampaignBody(slug, options, paths, rootDir) {
|
|
|
1462
1462
|
const usList = await readUsList(paths, slug);
|
|
1463
1463
|
|
|
1464
1464
|
if (usList.length === 0) {
|
|
1465
|
-
|
|
1465
|
+
// D-5 (dogfood): both leaders parse only H2 `## US-NNN:`. A common mistake is
|
|
1466
|
+
// authoring `### US-NNN` (H3+), which yields zero stories. Surface an actionable
|
|
1467
|
+
// hint instead of a bare "not found" (the zsh leader silently degrades here;
|
|
1468
|
+
// Node fail-closes — the safer behavior, now recoverable via `clean`).
|
|
1469
|
+
let hint = '';
|
|
1470
|
+
try {
|
|
1471
|
+
const prdRaw = await fs.readFile(paths.prdFile, 'utf8');
|
|
1472
|
+
if (/^#{3,}\s+US-\d{3}\b/m.test(prdRaw)) {
|
|
1473
|
+
hint = ' — found US-NNN heading(s) at level ### or deeper; US headings must be H2 ("## US-NNN:")';
|
|
1474
|
+
}
|
|
1475
|
+
} catch { /* best-effort hint */ }
|
|
1476
|
+
throw new Error(`No user stories found for ${slug}${hint}`);
|
|
1466
1477
|
}
|
|
1467
1478
|
|
|
1468
1479
|
if (!state.current_us) {
|
|
@@ -929,7 +929,9 @@ create_session() {
|
|
|
929
929
|
BASELINE_COMMIT=$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || echo "none")
|
|
930
930
|
|
|
931
931
|
# Truncate cost-log for fresh run (previous data in versioned campaign reports)
|
|
932
|
-
>
|
|
932
|
+
# NOTE: ': >' not bare '>' — in zsh a bare redirect with no command runs $NULLCMD
|
|
933
|
+
# (=cat), which blocks reading stdin when the leader has an open TTY (D-1 dogfood hang).
|
|
934
|
+
: > "$COST_LOG"
|
|
933
935
|
|
|
934
936
|
# v5.7 §4.2: WITH_SELF_VERIFICATION=1 is hard-rejected at script entry now,
|
|
935
937
|
# so by the time we reach create_session() the flag is guaranteed to be 0.
|
|
@@ -1853,8 +1855,8 @@ write_worker_trigger() {
|
|
|
1853
1855
|
else
|
|
1854
1856
|
echo "- **Test Spec**: Read \`$DESK/plans/test-spec-${SLUG}.md\` (full — find ${next_us} section)"
|
|
1855
1857
|
fi
|
|
1856
|
-
echo "When done,
|
|
1857
|
-
echo "
|
|
1858
|
+
echo "When done, you MUST WRITE (not just print) the verify signal to the iter-signal FILE at: ${SIGNAL_FILE}"
|
|
1859
|
+
echo "Write this exact JSON to that file (us_id=\"${next_us}\", not \"ALL\"): {\"iteration\": N, \"status\": \"verify\", \"us_id\": \"${next_us}\", \"summary\": \"what was done\", \"timestamp\": \"ISO\"}"
|
|
1858
1860
|
echo ""
|
|
1859
1861
|
echo "**Update the campaign memory's 'Next Iteration Contract' to reflect ${next_us}.**"
|
|
1860
1862
|
elif [[ -n "$VERIFIED_US" ]]; then
|