@lnilluv/pi-ralph-loop 0.1.4-dev.0 → 0.1.4-dev.1

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 (38) hide show
  1. package/README.md +63 -12
  2. package/package.json +1 -1
  3. package/src/index.ts +1034 -168
  4. package/src/ralph-draft-llm.ts +35 -7
  5. package/src/ralph-draft.ts +1 -1
  6. package/src/ralph.ts +708 -51
  7. package/src/runner-rpc.ts +434 -0
  8. package/src/runner-state.ts +822 -0
  9. package/src/runner.ts +957 -0
  10. package/tests/fixtures/parity/migrate/OPEN_QUESTIONS.md +3 -0
  11. package/tests/fixtures/parity/migrate/RALPH.md +27 -0
  12. package/tests/fixtures/parity/migrate/golden/MIGRATED.md +15 -0
  13. package/tests/fixtures/parity/migrate/legacy/source.md +6 -0
  14. package/tests/fixtures/parity/migrate/legacy/source.yaml +3 -0
  15. package/tests/fixtures/parity/migrate/scripts/show-legacy.sh +10 -0
  16. package/tests/fixtures/parity/migrate/scripts/verify.sh +15 -0
  17. package/tests/fixtures/parity/research/OPEN_QUESTIONS.md +3 -0
  18. package/tests/fixtures/parity/research/RALPH.md +45 -0
  19. package/tests/fixtures/parity/research/claim-evidence-checklist.md +15 -0
  20. package/tests/fixtures/parity/research/expected-outputs.md +22 -0
  21. package/tests/fixtures/parity/research/scripts/show-snapshots.sh +13 -0
  22. package/tests/fixtures/parity/research/scripts/verify.sh +55 -0
  23. package/tests/fixtures/parity/research/snapshots/app-factory-ai-cli.md +11 -0
  24. package/tests/fixtures/parity/research/snapshots/docs-factory-ai-cli-features-missions.md +11 -0
  25. package/tests/fixtures/parity/research/snapshots/factory-ai-news-missions.md +11 -0
  26. package/tests/fixtures/parity/research/source-manifest.md +20 -0
  27. package/tests/index.test.ts +3169 -104
  28. package/tests/parity/README.md +9 -0
  29. package/tests/parity/harness.py +526 -0
  30. package/tests/parity-harness.test.ts +42 -0
  31. package/tests/parity-research-fixture.test.ts +34 -0
  32. package/tests/ralph-draft-llm.test.ts +82 -9
  33. package/tests/ralph-draft.test.ts +1 -1
  34. package/tests/ralph.test.ts +1265 -36
  35. package/tests/runner-event-contract.test.ts +235 -0
  36. package/tests/runner-rpc.test.ts +358 -0
  37. package/tests/runner-state.test.ts +553 -0
  38. package/tests/runner.test.ts +1347 -0
@@ -12,6 +12,7 @@ import {
12
12
  parseRalphMarkdown,
13
13
  type DraftRequest,
14
14
  } from "../src/ralph.ts";
15
+ import { SECRET_PATH_POLICY_TOKEN } from "../src/secret-paths.ts";
15
16
  import {
16
17
  buildStrengtheningPrompt,
17
18
  strengthenDraftWithLlm,
@@ -138,6 +139,23 @@ test("buildStrengtheningPrompt includes the full prompt contract and repo signal
138
139
  assert.match(text, /return only a complete RALPH\.md/i);
139
140
  });
140
141
 
142
+ test("buildStrengtheningPrompt states the body-and-commands compatibility contract", () => {
143
+ const request = makeRequest();
144
+ const prompt = buildStrengtheningPrompt(request, "body-and-commands");
145
+ const text = promptText(prompt);
146
+
147
+ assert.match(text, /strengthening scope: body-and-commands/i);
148
+ assert.match(text, /body-and-commands scope/i);
149
+ assert.match(text, /command names and run strings must match the deterministic baseline exactly/i);
150
+ assert.match(text, /max_iterations may stay the same or decrease from the deterministic baseline, never increase/i);
151
+ assert.match(text, /top-level timeout may stay the same or decrease from the deterministic baseline, never increase/i);
152
+ assert.match(text, /per-command timeout may stay the same or decrease from that command's baseline timeout, and must still be <= timeout/i);
153
+ assert.match(text, /completion_promise must remain unchanged, including remaining absent when absent from the baseline/i);
154
+ assert.match(text, /every \{\{\s*commands\.<name>\s*\}\} must refer to an accepted command/i);
155
+ assert.match(text, /baseline guardrails remain fixed in this phase/i);
156
+ assert.match(text, /unsupported frontmatter changes are rejected and fall back automatically/i);
157
+ });
158
+
141
159
  test("buildStrengtheningPrompt omits secret-bearing top-level repo names", (t) => {
142
160
  const cwd = createTempRepo();
143
161
  t.after(() => rmSync(cwd, { recursive: true, force: true }));
@@ -319,11 +337,31 @@ test("strengthenDraftWithLlm normalizes a stronger full draft while preserving d
319
337
  });
320
338
  });
321
339
 
322
- test("strengthenDraftWithLlm accepts improved frontmatter and commands in body-and-commands scope", async () => {
340
+ test("strengthenDraftWithLlm accepts only compatible body-and-commands changes", async () => {
323
341
  const request = makeRequest();
324
342
  const runtime = makeRuntime();
325
343
  const baseline = parseRalphMarkdown(request.baselineDraft);
326
- const rawDraft = `---\ncommands:\n - name: smoke\n run: npm run smoke\n timeout: 45\nmax_iterations: 7\ntimeout: 120\nguardrails:\n block_commands:\n - git\\s+push\n protected_files:\n - .env*\n---\n${baseline.body}`;
344
+ const [testsCommand, , gitLogCommand] = baseline.frontmatter.commands;
345
+ const rawDraft = `---
346
+ commands:
347
+ - name: ${gitLogCommand.name}
348
+ run: ${gitLogCommand.run}
349
+ timeout: ${Math.max(1, gitLogCommand.timeout - 5)}
350
+ - name: ${testsCommand.name}
351
+ run: ${testsCommand.run}
352
+ timeout: ${Math.max(1, testsCommand.timeout - 15)}
353
+ max_iterations: ${Math.max(1, baseline.frontmatter.maxIterations - 5)}
354
+ timeout: ${Math.max(1, Math.min(baseline.frontmatter.timeout, 120))}
355
+ guardrails:
356
+ block_commands:
357
+ - 'git\\s+push'
358
+ protected_files:
359
+ - '${SECRET_PATH_POLICY_TOKEN}'
360
+ ---
361
+ ${baseline.body.replace(/\{\{ commands\.lint \}\}/g, "")}
362
+
363
+ Use {{ commands.${testsCommand.name} }} and {{ commands.${gitLogCommand.name} }}.
364
+ `;
327
365
 
328
366
  const result = await strengthenDraftWithLlm(request, runtime, {
329
367
  scope: "body-and-commands",
@@ -334,13 +372,14 @@ test("strengthenDraftWithLlm accepts improved frontmatter and commands in body-a
334
372
  const parsed = parseRalphMarkdown(result.draft.content);
335
373
 
336
374
  assert.deepEqual(result.draft.target, request.target);
337
- assert.deepEqual(parsed.frontmatter.commands, [{ name: "smoke", run: "npm run smoke", timeout: 45 }]);
338
- assert.equal(parsed.frontmatter.maxIterations, 7);
339
- assert.deepEqual(parsed.frontmatter.guardrails, {
340
- blockCommands: ["git\\s+push"],
341
- protectedFiles: [".env*"],
342
- });
343
- assert.equal(parsed.body.trim(), baseline.body.trim());
375
+ assert.deepEqual(parsed.frontmatter.commands, [
376
+ { name: gitLogCommand.name, run: gitLogCommand.run, timeout: Math.max(1, gitLogCommand.timeout - 5) },
377
+ { name: testsCommand.name, run: testsCommand.run, timeout: Math.max(1, testsCommand.timeout - 15) },
378
+ ]);
379
+ assert.equal(parsed.frontmatter.maxIterations, Math.max(1, baseline.frontmatter.maxIterations - 5));
380
+ assert.equal(parsed.frontmatter.timeout, Math.max(1, Math.min(baseline.frontmatter.timeout, 120)));
381
+ assert.deepEqual(parsed.frontmatter.guardrails, baseline.frontmatter.guardrails);
382
+ assert.equal(parsed.body.trim(), `${baseline.body.replace(/\{\{ commands\.lint \}\}/g, "").trim()}\n\nUse {{ commands.${testsCommand.name} }} and {{ commands.${gitLogCommand.name} }}.`.trim());
344
383
  assert.deepEqual(extractDraftMetadata(result.draft.content), {
345
384
  generator: "pi-ralph-loop",
346
385
  version: 2,
@@ -350,6 +389,40 @@ test("strengthenDraftWithLlm accepts improved frontmatter and commands in body-a
350
389
  });
351
390
  });
352
391
 
392
+ test("strengthenDraftWithLlm falls back when unsupported frontmatter changes are requested in body-and-commands scope", async () => {
393
+ const request = makeRequest();
394
+ const runtime = makeRuntime();
395
+ const baseline = parseRalphMarkdown(request.baselineDraft);
396
+ const [testsCommand, , gitLogCommand] = baseline.frontmatter.commands;
397
+ const rawDraft = `---
398
+ commands:
399
+ - name: ${gitLogCommand.name}
400
+ run: ${gitLogCommand.run}
401
+ timeout: ${gitLogCommand.timeout}
402
+ - name: ${testsCommand.name}
403
+ run: ${testsCommand.run}
404
+ timeout: ${testsCommand.timeout}
405
+ max_iterations: ${baseline.frontmatter.maxIterations}
406
+ timeout: ${baseline.frontmatter.timeout}
407
+ guardrails:
408
+ block_commands:
409
+ - 'git\\s+push'
410
+ - 'rm\\s+-rf'
411
+ protected_files: []
412
+ ---
413
+ ${baseline.body}
414
+
415
+ Use {{ commands.${testsCommand.name} }} and {{ commands.${gitLogCommand.name} }}.
416
+ `;
417
+
418
+ const result = await strengthenDraftWithLlm(request, runtime, {
419
+ scope: "body-and-commands",
420
+ completeImpl: async () => makeAssistantMessage([{ type: "text", text: rawDraft }]),
421
+ });
422
+
423
+ assert.deepEqual(result, { kind: "fallback" });
424
+ });
425
+
353
426
  test("strengthenDraftWithLlm falls back on invalid model output", async () => {
354
427
  const request = makeRequest();
355
428
  const runtime = makeRuntime();
@@ -134,7 +134,7 @@ test("createDraftPlan strengthens with an injected active model runtime", async
134
134
  strengthenCalls += 1;
135
135
  assert.equal(runtimeArg.model?.id, activeModel.id);
136
136
  assert.equal(runtimeArg.modelRegistry, runtime.modelRegistry);
137
- assert.equal(options?.scope, "body-only");
137
+ assert.equal(options?.scope, "body-and-commands");
138
138
  assert.match(request.baselineDraft, /reverse engineer this app/);
139
139
  return { kind: "llm-strengthened", draft: makeDraftPlan(task, target, "llm-strengthened", cwd) };
140
140
  },