@ai-dev-methodologies/rlp-desk 0.14.1 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-dev-methodologies/rlp-desk",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
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",
|
|
@@ -127,6 +127,11 @@ export async function pollForSignal(
|
|
|
127
127
|
capturePane = defaultCapturePane,
|
|
128
128
|
sendKeys = defaultSendKeys,
|
|
129
129
|
log = () => {},
|
|
130
|
+
// v0.14.2 Bug Report #4 Fix-D: optional legacy fallback path checked
|
|
131
|
+
// only after the canonical signalFile last-chance read fails. Caller
|
|
132
|
+
// (campaign-main-loop) supplies the pre-v0.13.0 .claude/ralph-desk
|
|
133
|
+
// memos path; signal-poller stays read-only and never migrates.
|
|
134
|
+
legacySignalFile = null,
|
|
130
135
|
} = {},
|
|
131
136
|
) {
|
|
132
137
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -249,7 +254,23 @@ export async function pollForSignal(
|
|
|
249
254
|
const rawContent = await readFile(signalFile);
|
|
250
255
|
return JSON.parse(rawContent);
|
|
251
256
|
} catch {
|
|
252
|
-
// fall through
|
|
257
|
+
// fall through
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// v0.14.2 Bug Report #4 Fix-D: codex sometimes lands the verdict at the
|
|
261
|
+
// pre-v0.13.0 legacy path (.claude/ralph-desk/memos/...) instead of the
|
|
262
|
+
// canonical .rlp-desk/memos/. If the caller passed `legacySignalFile`,
|
|
263
|
+
// try that path before declaring timeout — same semantics as the
|
|
264
|
+
// canonical last-chance read. campaign-main-loop is responsible for
|
|
265
|
+
// migrating the file into the canonical location after observing it;
|
|
266
|
+
// signal-poller stays read-only.
|
|
267
|
+
if (legacySignalFile) {
|
|
268
|
+
try {
|
|
269
|
+
const rawContent = await readFile(legacySignalFile);
|
|
270
|
+
return JSON.parse(rawContent);
|
|
271
|
+
} catch {
|
|
272
|
+
// fall through to TimeoutError
|
|
273
|
+
}
|
|
253
274
|
}
|
|
254
275
|
|
|
255
276
|
throw new TimeoutError(`Timed out waiting for valid JSON signal at ${signalFile}`);
|
|
@@ -180,6 +180,11 @@ export async function assembleVerifierPrompt({
|
|
|
180
180
|
verifiedUs = [],
|
|
181
181
|
autonomousMode = false,
|
|
182
182
|
conflictLogPath = '',
|
|
183
|
+
// v0.14.2 Bug Report #4 Fix-E: when supplied, the assembled prompt
|
|
184
|
+
// ends with a strong "MUST write verdict to <absolute_path>" rule so
|
|
185
|
+
// codex (which sometimes infers the legacy .claude/ralph-desk path
|
|
186
|
+
// from CWD) lands the verdict where the leader is polling.
|
|
187
|
+
verdictWritePath = '',
|
|
183
188
|
} = {}) {
|
|
184
189
|
const basePrompt = await readRequiredFile(promptBase, 'Verifier prompt base file');
|
|
185
190
|
const promptLines = [
|
|
@@ -209,5 +214,19 @@ export async function assembleVerifierPrompt({
|
|
|
209
214
|
appendAutonomousModeSection(promptLines, { conflictLogPath, verifier: true });
|
|
210
215
|
}
|
|
211
216
|
|
|
217
|
+
if (verdictWritePath) {
|
|
218
|
+
promptLines.push('');
|
|
219
|
+
promptLines.push('---');
|
|
220
|
+
promptLines.push('## CRITICAL: Verdict file write path (v0.14.2)');
|
|
221
|
+
promptLines.push('');
|
|
222
|
+
promptLines.push('Write `verify-verdict.json` ONLY to this absolute path:');
|
|
223
|
+
promptLines.push('');
|
|
224
|
+
promptLines.push(` ${verdictWritePath}`);
|
|
225
|
+
promptLines.push('');
|
|
226
|
+
promptLines.push('DO NOT write to `.claude/ralph-desk/memos/` — that path is deprecated since');
|
|
227
|
+
promptLines.push('v0.13.0. The leader polls only the absolute path above; writing elsewhere');
|
|
228
|
+
promptLines.push('triggers BLOCKED `verifier_dead` even though your verdict is correct.');
|
|
229
|
+
}
|
|
230
|
+
|
|
212
231
|
return `${promptLines.join('\n')}\n`;
|
|
213
232
|
}
|
|
@@ -43,6 +43,31 @@ const MODEL_UPGRADES = {
|
|
|
43
43
|
'gpt-5.3-codex-spark:xhigh': 'BLOCKED',
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
// v0.14.2 Bug Report #4 Fix-D: codex occasionally lands the verdict at the
|
|
47
|
+
// pre-v0.13.0 `.claude/ralph-desk/memos/` path despite prompt instructions.
|
|
48
|
+
// signal-poller's `legacySignalFile` last-chance branch returns the parsed
|
|
49
|
+
// verdict in memory; these two helpers move the file into the canonical
|
|
50
|
+
// .rlp-desk/memos/ location AFTER the polling loop succeeds, so analytics
|
|
51
|
+
// archival + sentinel hygiene remain consistent.
|
|
52
|
+
export async function _verdictMigrationNeeded(paths) {
|
|
53
|
+
if (!paths?.legacyVerdictFile || !paths?.verdictFile) return false;
|
|
54
|
+
// Migration is only meaningful when the legacy file exists AND the
|
|
55
|
+
// canonical file does not. If both exist, canonical wins (already
|
|
56
|
+
// observed) and we leave legacy alone.
|
|
57
|
+
let legacyExists = false;
|
|
58
|
+
let canonicalExists = false;
|
|
59
|
+
try { legacyExists = await exists(paths.legacyVerdictFile); } catch {}
|
|
60
|
+
try { canonicalExists = await exists(paths.verdictFile); } catch {}
|
|
61
|
+
return legacyExists && !canonicalExists;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function _migrateLegacyVerdict(paths) {
|
|
65
|
+
if (!paths?.legacyVerdictFile || !paths?.verdictFile) return false;
|
|
66
|
+
await fs.mkdir(path.dirname(paths.verdictFile), { recursive: true });
|
|
67
|
+
await fs.rename(paths.legacyVerdictFile, paths.verdictFile);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
46
71
|
// v0.13.0: legacy .claude/ralph-desk/ guidance for run mode (no auto-mv).
|
|
47
72
|
export function detectLegacyDeskInRunMode(rootDir, env = process.env) {
|
|
48
73
|
const legacyPath = path.join(rootDir, LEGACY_DESK_REL);
|
|
@@ -76,6 +101,12 @@ function buildPaths(rootDir, slug, env = process.env) {
|
|
|
76
101
|
doneClaimFile: path.join(deskRoot, 'memos', `${slug}-done-claim.json`),
|
|
77
102
|
signalFile: path.join(deskRoot, 'memos', `${slug}-iter-signal.json`),
|
|
78
103
|
verdictFile: path.join(deskRoot, 'memos', `${slug}-verify-verdict.json`),
|
|
104
|
+
// v0.14.2 Bug Report #4 Fix-D: codex sometimes lands the verdict at the
|
|
105
|
+
// pre-v0.13.0 legacy path. We track the absolute legacy location so the
|
|
106
|
+
// signal-poller last-chance read can fall back to it before declaring
|
|
107
|
+
// timeout. Always rooted at <project>/.claude/ralph-desk/memos/, even
|
|
108
|
+
// when RLP_DESK_RUNTIME_DIR overrides the canonical deskRoot.
|
|
109
|
+
legacyVerdictFile: path.join(rootDir, '.claude', 'ralph-desk', 'memos', `${slug}-verify-verdict.json`),
|
|
79
110
|
blockedSentinel: path.join(deskRoot, 'memos', `${slug}-blocked.md`),
|
|
80
111
|
completeSentinel: path.join(deskRoot, 'memos', `${slug}-complete.md`),
|
|
81
112
|
contextFile: path.join(deskRoot, 'context', `${slug}-latest.md`),
|
|
@@ -376,6 +407,11 @@ async function dispatchVerifier({
|
|
|
376
407
|
verifyMode: 'per-us',
|
|
377
408
|
usId,
|
|
378
409
|
verifiedUs: state.verified_us,
|
|
410
|
+
// v0.14.2 Fix-E: hand the absolute canonical verdict path to the
|
|
411
|
+
// verifier prompt. assembleVerifierPrompt appends a "CRITICAL: write
|
|
412
|
+
// verdict to <path>" footer so codex does not infer the legacy
|
|
413
|
+
// .claude/ralph-desk/memos/ location from CWD.
|
|
414
|
+
verdictWritePath: paths.verdictFile,
|
|
379
415
|
});
|
|
380
416
|
const fileName = suffix
|
|
381
417
|
? `${suffix}.verifier-prompt.md`
|
|
@@ -1452,7 +1488,21 @@ async function _runCampaignBody(slug, options, paths, rootDir) {
|
|
|
1452
1488
|
mode: parseModelFlag(verifierModel, 'verifier').engine,
|
|
1453
1489
|
paneId: state.verifier_pane_id,
|
|
1454
1490
|
timeoutMs: iterTimeoutMs,
|
|
1491
|
+
// v0.14.2 Fix-D: codex sometimes writes the verdict at the legacy
|
|
1492
|
+
// .claude/ralph-desk/memos/ path. signal-poller's last-chance read
|
|
1493
|
+
// tries this fallback before timing out.
|
|
1494
|
+
legacySignalFile: paths.legacyVerdictFile,
|
|
1455
1495
|
});
|
|
1496
|
+
// v0.14.2 Fix-D continued: if the verdict came from the legacy path,
|
|
1497
|
+
// migrate it into the canonical location so the rest of the pipeline
|
|
1498
|
+
// (analytics archival, sentinels, status) sees a single canonical
|
|
1499
|
+
// file. Best-effort — any rename failure is logged but does not
|
|
1500
|
+
// re-throw because we already have the parsed verdict in memory.
|
|
1501
|
+
if (await _verdictMigrationNeeded(paths)) {
|
|
1502
|
+
await _migrateLegacyVerdict(paths).catch((migrateErr) => {
|
|
1503
|
+
console.error('[v0.14.2] legacy verdict migration failed:', migrateErr?.message ?? migrateErr);
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1456
1506
|
validateArtifact(verdict, {
|
|
1457
1507
|
expectedSlug: slug,
|
|
1458
1508
|
iterationFloor: state.iteration,
|
|
@@ -36,13 +36,22 @@ const DEFAULT_NO_RE = /\[y\/N\]|\(yes\/no,\s*default\s+no\)|[Dd]efault[: ]+[Nn]o
|
|
|
36
36
|
const ACTIVE_TASK_RE =
|
|
37
37
|
/esc to interrupt|background terminal running|^\s*[·✻]\s+[A-Za-z]+(\.{3}|…)/m;
|
|
38
38
|
|
|
39
|
-
// v0.14.1: codex post-work idle UI markers. NOT a permission prompt
|
|
40
|
-
// codex CLI has finished its task and is waiting for the next user
|
|
41
|
-
//
|
|
42
|
-
// a status bar "Context X% left". Sources: BOS Bug Report #3 (2026-05-04).
|
|
39
|
+
// v0.14.1 / v0.14.2: codex post-work idle UI markers. NOT a permission prompt
|
|
40
|
+
// — the codex CLI has finished its task and is waiting for the next user
|
|
41
|
+
// input. Sources: BOS Bug Report #3 (2026-05-04) + #4 (2026-05-05).
|
|
43
42
|
// Treat this as "task done, idle awaiting input"; callers should harvest
|
|
44
43
|
// the verdict file rather than escalate as `prompt_blocked`.
|
|
45
|
-
|
|
44
|
+
//
|
|
45
|
+
// v0.14.2 relaxation (Bug #4): the v0.14.1 strict "─ Worked for Xm Ys ─"
|
|
46
|
+
// regex required the surrounding horizontal rule to match. tmux capture
|
|
47
|
+
// truncation occasionally dropped those rules so the pattern missed in
|
|
48
|
+
// production. Match on multiple independent markers; ANY one is enough.
|
|
49
|
+
// 1. "Worked for Xm Ys" — duration line, codex-only
|
|
50
|
+
// 2. "Context X%left" (no space) — status bar; tolerate wrap removal
|
|
51
|
+
// 3. "gpt-X.Y reasoning · branch" — codex idle status line
|
|
52
|
+
// 4. codex default suggestions — only printed at the idle prompt
|
|
53
|
+
export const CODEX_IDLE_RE =
|
|
54
|
+
/Worked for \d+m \d+s|Context \d+%\s*left|gpt-\d+(\.\d+)? (low|medium|high|xhigh) ·|Improve documentation in @|Summarize recent commits|Explain (this )?code/;
|
|
46
55
|
export function isCodexIdleUi(paneText) {
|
|
47
56
|
if (typeof paneText !== 'string' || paneText.length === 0) return false;
|
|
48
57
|
return CODEX_IDLE_RE.test(paneText);
|