@pareto4/pp-core 1.20.0 → 1.21.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.
Files changed (57) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/agents/pp-nyquist-auditor.md +19 -14
  3. package/agents/pp-plan-checker.md +59 -36
  4. package/agents/pp-planner.md +34 -53
  5. package/agents/pp-verifier.md +3 -52
  6. package/commands/pp-autonomous.md +2 -1
  7. package/commands/pp-plan-phase.md +2 -1
  8. package/commands/pp-plan-review-convergence.md +2 -1
  9. package/gemini-extension.json +1 -1
  10. package/package.json +1 -1
  11. package/pp-core/bin/lib/capability-activation.cjs +77 -0
  12. package/pp-core/bin/lib/capability-registry.cjs +14 -14
  13. package/pp-core/bin/lib/capability-state.cjs +8 -18
  14. package/pp-core/bin/lib/ceremony.cjs +1342 -0
  15. package/pp-core/bin/lib/check-command-router.cjs +62 -1
  16. package/pp-core/bin/lib/commands.cjs +29 -3
  17. package/pp-core/bin/lib/config.cjs +5 -0
  18. package/pp-core/bin/lib/init.cjs +50 -1
  19. package/pp-core/bin/lib/io.cjs +2 -0
  20. package/pp-core/bin/lib/loop-resolver.cjs +26 -12
  21. package/pp-core/bin/lib/phase.cjs +39 -3
  22. package/pp-core/bin/lib/plan-sizing.cjs +346 -0
  23. package/pp-core/bin/lib/test-substance.cjs +3190 -0
  24. package/pp-core/bin/lib/timeline.cjs +605 -0
  25. package/pp-core/bin/pp-tools.cjs +71 -4
  26. package/pp-core/bin/shared/ceremony-gates.json +17 -0
  27. package/pp-core/bin/shared/config-schema.manifest.json +1 -0
  28. package/pp-core/references/ceremony.md +81 -0
  29. package/pp-core/references/few-shot-examples/plan-checker.md +8 -8
  30. package/pp-core/references/phase-timeline.md +107 -0
  31. package/pp-core/references/planner-antipatterns.md +32 -0
  32. package/pp-core/references/planner-guidance.md +17 -14
  33. package/pp-core/references/planner-revision.md +1 -1
  34. package/pp-core/references/planning-config.md +3 -2
  35. package/pp-core/references/test-substance.md +324 -0
  36. package/pp-core/references/verifier-stub-patterns.md +51 -0
  37. package/pp-core/templates/VALIDATION.md +1 -0
  38. package/pp-core/templates/phase-prompt.md +19 -7
  39. package/pp-core/templates/roadmap.md +1 -1
  40. package/pp-core/templates/verification-report.md +1 -1
  41. package/pp-core/workflows/audit-milestone.md +4 -1
  42. package/pp-core/workflows/autonomous.md +11 -8
  43. package/pp-core/workflows/code-review.md +15 -6
  44. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +7 -7
  45. package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -2
  46. package/pp-core/workflows/execute-phase.md +14 -3
  47. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +7 -3
  48. package/pp-core/workflows/plan-phase/steps/prd-express.md +104 -0
  49. package/pp-core/workflows/plan-phase.md +47 -114
  50. package/pp-core/workflows/plan-review-convergence.md +6 -3
  51. package/pp-core/workflows/ship.md +2 -2
  52. package/pp-core/workflows/stats.md +19 -4
  53. package/pp-core/workflows/validate-phase.md +19 -4
  54. package/pp-core/workflows/verify-phase.md +1 -1
  55. package/pp-core/workflows/verify-work.md +1 -1
  56. package/scripts/gen-capability-registry.cjs +43 -45
  57. package/scripts/run-tests.cjs +54 -30
@@ -31,7 +31,7 @@
31
31
 
32
32
  const { readdirSync } = require('fs');
33
33
  const { join, basename } = require('path');
34
- const { execFileSync } = require('child_process');
34
+ const { execFileSync, spawnSync } = require('child_process');
35
35
  const { ExitError, runMain } = require('./lib/cli-exit.cjs');
36
36
 
37
37
  const SUITES = ['all', 'unit', 'integration', 'install', 'security', 'slow'];
@@ -548,42 +548,66 @@ function main() {
548
548
  if (chunks.length > 1) {
549
549
  console.error(`run-tests: chunk ${i + 1}/${chunks.length} — ${chunks[i].length} files`);
550
550
  }
551
- try {
552
- execFileSync(
553
- process.execPath,
554
- ['--test', ...(forceExit ? ['--test-force-exit'] : []), concurrency, ...chunks[i]],
555
- {
556
- stdio: 'inherit',
557
- env: { ...process.env },
558
- timeout: chunkTimeoutMs,
559
- },
551
+ const chunkFiles = chunks[i].map(f => f.split(/[\\/]/).pop()).join(' ');
552
+ // stdout is captured, not inherited, so the chunk's node:test summary can be
553
+ // checked; it is written through unchanged once the chunk ends. stderr stays
554
+ // live. A chunk can exit 0 with its output cut off and no summary — on CI
555
+ // (2026-10-28) two of five chunks did, and ~1,000 tests never reported while
556
+ // the job stayed green — so a missing summary fails the run.
557
+ const result = spawnSync(
558
+ process.execPath,
559
+ ['--test', ...(forceExit ? ['--test-force-exit'] : []), concurrency, ...chunks[i]],
560
+ {
561
+ stdio: ['inherit', 'pipe', 'inherit'],
562
+ env: { ...process.env },
563
+ timeout: chunkTimeoutMs,
564
+ maxBuffer: 1024 * 1024 * 1024,
565
+ },
566
+ );
567
+ if (result.stdout && result.stdout.length > 0) process.stdout.write(result.stdout);
568
+ // When the per-chunk timeout fires, spawnSync kills the child and reports
569
+ // error.code === 'ETIMEDOUT' (POSIX) and/or a kill signal (platform-dependent).
570
+ // Check both so detection holds on Windows and POSIX.
571
+ const timedOut = (result.error && result.error.code === 'ETIMEDOUT') ||
572
+ (result.signal !== null && result.status === null && !result.error);
573
+ let code = result.status === null ? 1 : result.status;
574
+ if (result.error && result.error.code === 'ETIMEDOUT') code = 1;
575
+ if (timedOut) {
576
+ console.error(
577
+ `run-tests: chunk ${i + 1}/${chunks.length} exceeded the per-chunk timeout ` +
578
+ `of ${chunkTimeoutMs}ms and was killed — a test in this chunk is likely leaking ` +
579
+ `an open handle (un-terminated Worker, un-killed child process, or ref'd timer) ` +
580
+ `so node --test never exits. Files: ${chunkFiles}`,
560
581
  );
561
- } catch (err) {
562
- // When the per-chunk timeout fires, execFileSync kills the child and
563
- // surfaces it as err.code === 'ETIMEDOUT' (POSIX) and/or err.killed === true
564
- // (platform-dependent). Check both so detection holds on Windows and POSIX.
565
- const timedOut = err.killed === true || err.code === 'ETIMEDOUT';
566
- if (timedOut) {
567
- console.error(
568
- `run-tests: chunk ${i + 1}/${chunks.length} exceeded the per-chunk timeout ` +
569
- `of ${chunkTimeoutMs}ms and was killed — a test in this chunk is likely leaking ` +
570
- `an open handle (un-terminated Worker, un-killed child process, or ref'd timer) ` +
571
- `so node --test never exits. Files: ${chunks[i]
572
- .map(f => f.split(/[\\/]/).pop())
573
- .join(' ')}`,
574
- );
575
- }
576
- const code = err.status || 1;
577
- // Run every chunk so the operator sees all failures in one pass; report
578
- // the first non-zero exit at the end.
579
- if (firstFailureExit === 0) firstFailureExit = code;
582
+ } else if (result.error) {
583
+ console.error(`run-tests: chunk ${i + 1}/${chunks.length} could not run: ${result.error.message}`);
584
+ code = 1;
585
+ } else if (code === 0 && !hasTestSummary(result.stdout)) {
586
+ console.error(
587
+ `run-tests: chunk ${i + 1}/${chunks.length} exited without a test summary ` +
588
+ `(no "ℹ tests N" / "ℹ fail N" lines) — its output was cut off, so some of its ` +
589
+ `tests never reported. Files: ${chunkFiles}`,
590
+ );
591
+ code = 1;
580
592
  }
593
+ // Run every chunk so the operator sees all failures in one pass; report
594
+ // the first non-zero exit at the end.
595
+ if (code !== 0 && firstFailureExit === 0) firstFailureExit = code;
581
596
  }
582
597
  if (firstFailureExit !== 0) return firstFailureExit;
583
598
  }
584
599
 
600
+ /**
601
+ * Whether a chunk's stdout carries node:test's closing summary: both the
602
+ * `tests N` and `fail N` lines, from the spec reporter (`ℹ`) or TAP (`#`).
603
+ */
604
+ function hasTestSummary(stdout) {
605
+ const text = stdout ? stdout.toString('utf8') : '';
606
+ return /^(?:ℹ|#) tests \d+\s*$/m.test(text) && /^(?:ℹ|#) fail \d+\s*$/m.test(text);
607
+ }
608
+
585
609
  if (require.main === module) {
586
610
  runMain(main);
587
611
  }
588
612
 
589
- module.exports = { suiteOf, ensureBuiltArtifacts, parseShardArg, selectShard };
613
+ module.exports = { suiteOf, ensureBuiltArtifacts, parseShardArg, selectShard, hasTestSummary };