@ai-dev-methodologies/rlp-desk 0.15.4 → 0.15.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/package.json +9 -3
  3. package/src/node/MANIFEST.txt +3 -0
  4. package/src/node/prompts/prompt-assembler.mjs +2 -2
  5. package/src/node/run.mjs +70 -3
  6. package/src/node/runner/campaign-main-loop.mjs +13 -2
  7. package/src/scripts/run_ralph_desk.zsh +5 -3
  8. package/docs/rlp-desk/internal/verification-policy-gap-analysis.md +0 -523
  9. package/docs/rlp-desk/internal/verification-strategy-research.md +0 -2097
  10. package/docs/rlp-desk/plans/cozy-gliding-trinket.md +0 -53
  11. package/docs/rlp-desk/plans/frolicking-churning-honey.md +0 -253
  12. package/docs/rlp-desk/plans/keen-sauteeing-snowflake.md +0 -245
  13. package/docs/rlp-desk/plans/mutable-booping-corbato.md +0 -163
  14. package/docs/rlp-desk/plans/rlp-desk-0.11-handoff-7fixes.md +0 -352
  15. package/docs/rlp-desk/plans/rlp-desk-0.11.1-tmux-pane-disappearance.md +0 -260
  16. package/docs/rlp-desk/plans/rlp-desk-elegant-papert-agent-a8cd695ffca2a3ad8.md +0 -84
  17. package/docs/rlp-desk/plans/rlp-desk-elegant-papert.md +0 -270
  18. package/docs/rlp-desk/plans/rlp-desk-tmux-flywheel-routing.md +0 -730
  19. package/docs/rlp-desk/plans/toasty-whistling-diffie-agent-a6814625642e956da.md +0 -201
  20. package/docs/rlp-desk/plans/toasty-whistling-diffie.md +0 -117
  21. package/docs/rlp-desk/plans/validated-snacking-crayon.md +0 -204
  22. package/examples/calculator/.claude/ralph-desk/logs/loop-test/iter-001.worker-output.log +0 -0
  23. package/examples/calculator/.claude/ralph-desk/logs/loop-test/iter-001.worker-prompt.md +0 -38
  24. package/examples/calculator/.claude/ralph-desk/logs/loop-test/iter-001.worker-trigger.sh +0 -28
  25. package/examples/calculator/.claude/ralph-desk/logs/loop-test/session-config.json +0 -25
  26. package/examples/calculator/.claude/ralph-desk/logs/loop-test/status.json +0 -10
  27. package/examples/calculator/.claude/ralph-desk/logs/loop-test/worker-heartbeat.json +0 -1
package/CHANGELOG.md CHANGED
@@ -11,7 +11,23 @@ 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.4] — 2026-05-08 (pending release)
14
+ ## [0.15.5] — 2026-06-17
15
+
16
+ Patch: fixes surfaced by a fresh-context live dogfood of the tmux and agent run modes, plus packaging hygiene.
17
+
18
+ ### Fixed
19
+ - **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.)
20
+ - **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"`.
21
+ - **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).
22
+ - **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".
23
+
24
+ ### Added
25
+ - **`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.
26
+
27
+ ### Changed
28
+ - **Smaller published package.** The tarball no longer ships internal research docs, dev planning handoffs, or example runtime state (73 → 53 files, ~364 → ~244 kB).
29
+
30
+ ## [0.15.4] — 2026-05-08
15
31
 
16
32
  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
33
 
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@ai-dev-methodologies/rlp-desk",
3
- "version": "0.15.4",
3
+ "version": "0.15.5",
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
- "examples/",
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",
@@ -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
@@ -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, signal verify with us_id="${nextUs}" (not "ALL").`);
154
- promptLines.push(`Signal format: {"iteration": N, "status": "verify", "us_id": "${nextUs}", ...}`);
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 (not implemented in the Node rewrite yet)',
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
- throw new Error(`No user stories found for ${slug}`);
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
- > "$COST_LOG"
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, signal verify with us_id=\"${next_us}\" (not \"ALL\")."
1857
- echo "Signal format: {\"iteration\": N, \"status\": \"verify\", \"us_id\": \"${next_us}\", ...}"
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