@pareto4/pp-core 1.5.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 (618) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/GEMINI.md +53 -0
  3. package/LICENSE +22 -0
  4. package/README.md +100 -0
  5. package/agents/pp-advisor-researcher.md +108 -0
  6. package/agents/pp-ai-researcher.md +114 -0
  7. package/agents/pp-assumptions-analyzer.md +105 -0
  8. package/agents/pp-code-fixer.md +669 -0
  9. package/agents/pp-code-reviewer.md +387 -0
  10. package/agents/pp-codebase-mapper.md +853 -0
  11. package/agents/pp-debug-session-manager.md +315 -0
  12. package/agents/pp-debugger.md +1453 -0
  13. package/agents/pp-doc-classifier.md +168 -0
  14. package/agents/pp-doc-synthesizer.md +204 -0
  15. package/agents/pp-doc-verifier.md +217 -0
  16. package/agents/pp-doc-writer.md +616 -0
  17. package/agents/pp-domain-researcher.md +147 -0
  18. package/agents/pp-eval-auditor.md +191 -0
  19. package/agents/pp-eval-planner.md +154 -0
  20. package/agents/pp-executor.md +808 -0
  21. package/agents/pp-framework-selector.md +160 -0
  22. package/agents/pp-integration-checker.md +470 -0
  23. package/agents/pp-intel-updater.md +339 -0
  24. package/agents/pp-mempalace-curator.md +47 -0
  25. package/agents/pp-nyquist-auditor.md +204 -0
  26. package/agents/pp-pattern-mapper.md +335 -0
  27. package/agents/pp-phase-researcher.md +867 -0
  28. package/agents/pp-plan-checker.md +988 -0
  29. package/agents/pp-planner.md +1052 -0
  30. package/agents/pp-project-researcher.md +612 -0
  31. package/agents/pp-research-synthesizer.md +260 -0
  32. package/agents/pp-roadmapper.md +741 -0
  33. package/agents/pp-security-auditor.md +156 -0
  34. package/agents/pp-ui-auditor.md +495 -0
  35. package/agents/pp-ui-checker.md +309 -0
  36. package/agents/pp-ui-researcher.md +375 -0
  37. package/agents/pp-user-profiler.md +171 -0
  38. package/agents/pp-verifier.md +967 -0
  39. package/assets/gsd-logo-2000-transparent.png +0 -0
  40. package/assets/gsd-logo-2000-transparent.svg +17 -0
  41. package/assets/gsd-logo-2000.png +0 -0
  42. package/assets/gsd-logo-2000.svg +21 -0
  43. package/assets/terminal.svg +68 -0
  44. package/bin/install.js +12370 -0
  45. package/bin/lib/ui-safety-gate.cjs +107 -0
  46. package/commands/pp-add-tests.md +42 -0
  47. package/commands/pp-ai-integration-phase.md +37 -0
  48. package/commands/pp-audit-fix.md +34 -0
  49. package/commands/pp-audit-milestone.md +37 -0
  50. package/commands/pp-audit-uat.md +24 -0
  51. package/commands/pp-autonomous.md +51 -0
  52. package/commands/pp-capture.md +62 -0
  53. package/commands/pp-cleanup.md +24 -0
  54. package/commands/pp-code-review.md +59 -0
  55. package/commands/pp-complete-milestone.md +143 -0
  56. package/commands/pp-config.md +56 -0
  57. package/commands/pp-debug.md +52 -0
  58. package/commands/pp-discovery.md +34 -0
  59. package/commands/pp-discuss-phase.md +77 -0
  60. package/commands/pp-docs-update.md +49 -0
  61. package/commands/pp-eval-review.md +33 -0
  62. package/commands/pp-execute-phase.md +65 -0
  63. package/commands/pp-explore.md +27 -0
  64. package/commands/pp-extract-learnings.md +23 -0
  65. package/commands/pp-fast.md +31 -0
  66. package/commands/pp-forensics.md +57 -0
  67. package/commands/pp-graphify.md +204 -0
  68. package/commands/pp-health.md +31 -0
  69. package/commands/pp-help.md +28 -0
  70. package/commands/pp-import.md +45 -0
  71. package/commands/pp-inbox.md +39 -0
  72. package/commands/pp-ingest-docs.md +42 -0
  73. package/commands/pp-manager.md +45 -0
  74. package/commands/pp-map-codebase.md +83 -0
  75. package/commands/pp-mempalace-capture.md +71 -0
  76. package/commands/pp-mempalace-recall.md +102 -0
  77. package/commands/pp-milestone-summary.md +51 -0
  78. package/commands/pp-mvp-phase.md +45 -0
  79. package/commands/pp-new-milestone.md +45 -0
  80. package/commands/pp-new-project.md +47 -0
  81. package/commands/pp-ns-context.md +25 -0
  82. package/commands/pp-ns-ideate.md +24 -0
  83. package/commands/pp-ns-manage.md +36 -0
  84. package/commands/pp-ns-project.md +27 -0
  85. package/commands/pp-ns-review.md +29 -0
  86. package/commands/pp-ns-workflow.md +35 -0
  87. package/commands/pp-pause-work.md +43 -0
  88. package/commands/pp-phase.md +56 -0
  89. package/commands/pp-plan-phase.md +63 -0
  90. package/commands/pp-plan-review-convergence.md +60 -0
  91. package/commands/pp-pr-branch.md +26 -0
  92. package/commands/pp-profile-user.md +46 -0
  93. package/commands/pp-progress.md +49 -0
  94. package/commands/pp-quick.md +174 -0
  95. package/commands/pp-resume-work.md +30 -0
  96. package/commands/pp-review-backlog.md +63 -0
  97. package/commands/pp-review.md +42 -0
  98. package/commands/pp-secure-phase.md +36 -0
  99. package/commands/pp-settings.md +29 -0
  100. package/commands/pp-ship.md +24 -0
  101. package/commands/pp-sketch.md +60 -0
  102. package/commands/pp-spec-phase.md +63 -0
  103. package/commands/pp-spike.md +57 -0
  104. package/commands/pp-stats.md +20 -0
  105. package/commands/pp-surface.md +162 -0
  106. package/commands/pp-thread.md +24 -0
  107. package/commands/pp-ui-phase.md +35 -0
  108. package/commands/pp-ui-review.md +33 -0
  109. package/commands/pp-ultraplan-phase.md +34 -0
  110. package/commands/pp-undo.md +35 -0
  111. package/commands/pp-update.md +49 -0
  112. package/commands/pp-validate-phase.md +36 -0
  113. package/commands/pp-verify-work.md +39 -0
  114. package/commands/pp-workspace.md +52 -0
  115. package/commands/pp-workstreams.md +70 -0
  116. package/gemini-extension.json +6 -0
  117. package/hooks/dist/lib/git-cmd.js +150 -0
  118. package/hooks/dist/lib/pp-graphify-rebuild.sh +65 -0
  119. package/hooks/dist/managed-hooks-registry.cjs +39 -0
  120. package/hooks/dist/pp-check-update-worker.js +108 -0
  121. package/hooks/dist/pp-check-update.js +66 -0
  122. package/hooks/dist/pp-config-reload.js +133 -0
  123. package/hooks/dist/pp-context-monitor.js +196 -0
  124. package/hooks/dist/pp-cursor-post-tool.js +75 -0
  125. package/hooks/dist/pp-cursor-session-start.js +52 -0
  126. package/hooks/dist/pp-ensure-canonical-path.js +305 -0
  127. package/hooks/dist/pp-graphify-update.sh +158 -0
  128. package/hooks/dist/pp-phase-boundary.sh +47 -0
  129. package/hooks/dist/pp-prompt-guard.js +97 -0
  130. package/hooks/dist/pp-read-guard.js +101 -0
  131. package/hooks/dist/pp-read-injection-scanner.js +203 -0
  132. package/hooks/dist/pp-session-state.sh +59 -0
  133. package/hooks/dist/pp-statusline.js +566 -0
  134. package/hooks/dist/pp-update-banner.js +138 -0
  135. package/hooks/dist/pp-validate-commit.sh +57 -0
  136. package/hooks/dist/pp-workflow-guard.js +167 -0
  137. package/hooks/dist/pp-worktree-path-guard.js +185 -0
  138. package/hooks/hooks.json +70 -0
  139. package/hooks/lib/git-cmd.js +150 -0
  140. package/hooks/lib/pp-graphify-rebuild.sh +65 -0
  141. package/hooks/managed-hooks-registry.cjs +39 -0
  142. package/hooks/pp-check-update-worker.js +108 -0
  143. package/hooks/pp-check-update.js +66 -0
  144. package/hooks/pp-config-reload.js +133 -0
  145. package/hooks/pp-context-monitor.js +196 -0
  146. package/hooks/pp-cursor-post-tool.js +75 -0
  147. package/hooks/pp-cursor-session-start.js +52 -0
  148. package/hooks/pp-ensure-canonical-path.js +305 -0
  149. package/hooks/pp-graphify-update.sh +158 -0
  150. package/hooks/pp-phase-boundary.sh +47 -0
  151. package/hooks/pp-prompt-guard.js +97 -0
  152. package/hooks/pp-read-guard.js +101 -0
  153. package/hooks/pp-read-injection-scanner.js +203 -0
  154. package/hooks/pp-session-state.sh +59 -0
  155. package/hooks/pp-statusline.js +566 -0
  156. package/hooks/pp-update-banner.js +138 -0
  157. package/hooks/pp-validate-commit.sh +57 -0
  158. package/hooks/pp-workflow-guard.js +167 -0
  159. package/hooks/pp-worktree-path-guard.js +185 -0
  160. package/package.json +125 -0
  161. package/pp-core/bin/check-latest-version.cjs +161 -0
  162. package/pp-core/bin/lib/active-workstream-store.cjs +297 -0
  163. package/pp-core/bin/lib/adr-parser.cjs +399 -0
  164. package/pp-core/bin/lib/agent-command-router.cjs +68 -0
  165. package/pp-core/bin/lib/agent-install-check.cjs +143 -0
  166. package/pp-core/bin/lib/artifacts.cjs +51 -0
  167. package/pp-core/bin/lib/audit-command-router.cjs +61 -0
  168. package/pp-core/bin/lib/audit.cjs +743 -0
  169. package/pp-core/bin/lib/capability-activation.cjs +113 -0
  170. package/pp-core/bin/lib/capability-registry.cjs +3727 -0
  171. package/pp-core/bin/lib/capability-state.cjs +497 -0
  172. package/pp-core/bin/lib/capability-writer.cjs +355 -0
  173. package/pp-core/bin/lib/check-command-router.cjs +800 -0
  174. package/pp-core/bin/lib/cjs-command-router-adapter.cjs +81 -0
  175. package/pp-core/bin/lib/cli-exit.cjs +61 -0
  176. package/pp-core/bin/lib/clock.cjs +95 -0
  177. package/pp-core/bin/lib/clusters.cjs +131 -0
  178. package/pp-core/bin/lib/code-review-flags.cjs +59 -0
  179. package/pp-core/bin/lib/command-aliases.cjs +809 -0
  180. package/pp-core/bin/lib/command-arg-projection.cjs +55 -0
  181. package/pp-core/bin/lib/command-roster.cjs +19 -0
  182. package/pp-core/bin/lib/command-routing-hub.cjs +300 -0
  183. package/pp-core/bin/lib/commands.cjs +1244 -0
  184. package/pp-core/bin/lib/config-loader.cjs +714 -0
  185. package/pp-core/bin/lib/config-schema.cjs +58 -0
  186. package/pp-core/bin/lib/config-types.cjs +20 -0
  187. package/pp-core/bin/lib/config.cjs +796 -0
  188. package/pp-core/bin/lib/configuration.cjs +209 -0
  189. package/pp-core/bin/lib/context-utilization.cjs +48 -0
  190. package/pp-core/bin/lib/core-utils.cjs +198 -0
  191. package/pp-core/bin/lib/decisions.cjs +136 -0
  192. package/pp-core/bin/lib/docs.cjs +264 -0
  193. package/pp-core/bin/lib/drift.cjs +364 -0
  194. package/pp-core/bin/lib/edge-probe.cjs +196 -0
  195. package/pp-core/bin/lib/fallow-runner.cjs +153 -0
  196. package/pp-core/bin/lib/federated-config.cjs +182 -0
  197. package/pp-core/bin/lib/frontmatter.cjs +494 -0
  198. package/pp-core/bin/lib/gap-checker.cjs +260 -0
  199. package/pp-core/bin/lib/git-base-branch.cjs +220 -0
  200. package/pp-core/bin/lib/graphify-command-router.cjs +72 -0
  201. package/pp-core/bin/lib/graphify.cjs +472 -0
  202. package/pp-core/bin/lib/gsd2-import.cjs +456 -0
  203. package/pp-core/bin/lib/init-command-router.cjs +62 -0
  204. package/pp-core/bin/lib/init.cjs +1912 -0
  205. package/pp-core/bin/lib/install-profiles.cjs +814 -0
  206. package/pp-core/bin/lib/installer-migration-authoring.cjs +122 -0
  207. package/pp-core/bin/lib/installer-migration-report.cjs +351 -0
  208. package/pp-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +225 -0
  209. package/pp-core/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +48 -0
  210. package/pp-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +94 -0
  211. package/pp-core/bin/lib/installer-migrations/003-rename-get-shit-done-to-gsd-core.cjs +108 -0
  212. package/pp-core/bin/lib/installer-migrations/004-prune-stale-pristine-snapshots.cjs +123 -0
  213. package/pp-core/bin/lib/installer-migrations/005-rename-gsd-core-to-pp-core.cjs +109 -0
  214. package/pp-core/bin/lib/installer-migrations/006-rename-gsd-command-namespace.cjs +135 -0
  215. package/pp-core/bin/lib/installer-migrations.cjs +823 -0
  216. package/pp-core/bin/lib/intel-command-router.cjs +119 -0
  217. package/pp-core/bin/lib/intel.cjs +584 -0
  218. package/pp-core/bin/lib/io.cjs +220 -0
  219. package/pp-core/bin/lib/learnings.cjs +270 -0
  220. package/pp-core/bin/lib/legacy-cleanup.cjs +253 -0
  221. package/pp-core/bin/lib/loop-host-contract.cjs +105 -0
  222. package/pp-core/bin/lib/loop-resolver.cjs +479 -0
  223. package/pp-core/bin/lib/milestone.cjs +381 -0
  224. package/pp-core/bin/lib/model-catalog.cjs +154 -0
  225. package/pp-core/bin/lib/model-profiles.cjs +24 -0
  226. package/pp-core/bin/lib/model-resolver.cjs +467 -0
  227. package/pp-core/bin/lib/observability/event.cjs +51 -0
  228. package/pp-core/bin/lib/observability/logger.cjs +146 -0
  229. package/pp-core/bin/lib/observability/redaction.cjs +48 -0
  230. package/pp-core/bin/lib/package-identity.cjs +35 -0
  231. package/pp-core/bin/lib/package-legitimacy.cjs +368 -0
  232. package/pp-core/bin/lib/phase-command-router.cjs +209 -0
  233. package/pp-core/bin/lib/phase-id.cjs +213 -0
  234. package/pp-core/bin/lib/phase-lifecycle.cjs +74 -0
  235. package/pp-core/bin/lib/phase-locator.cjs +145 -0
  236. package/pp-core/bin/lib/phase.cjs +1446 -0
  237. package/pp-core/bin/lib/phases-command-router.cjs +43 -0
  238. package/pp-core/bin/lib/plan-drift-guard.cjs +117 -0
  239. package/pp-core/bin/lib/plan-scan.cjs +91 -0
  240. package/pp-core/bin/lib/planning-workspace.cjs +245 -0
  241. package/pp-core/bin/lib/probe-core.cjs +402 -0
  242. package/pp-core/bin/lib/profile-output.cjs +1164 -0
  243. package/pp-core/bin/lib/profile-pipeline-command-router.cjs +138 -0
  244. package/pp-core/bin/lib/profile-pipeline.cjs +519 -0
  245. package/pp-core/bin/lib/prohibition-enforcement.cjs +660 -0
  246. package/pp-core/bin/lib/project-root.cjs +119 -0
  247. package/pp-core/bin/lib/prompt-budget.cjs +305 -0
  248. package/pp-core/bin/lib/research-provider.cjs +137 -0
  249. package/pp-core/bin/lib/research-store.cjs +167 -0
  250. package/pp-core/bin/lib/review-reviewer-selection.cjs +121 -0
  251. package/pp-core/bin/lib/roadmap-command-router.cjs +166 -0
  252. package/pp-core/bin/lib/roadmap-parser.cjs +456 -0
  253. package/pp-core/bin/lib/roadmap-upgrade.cjs +476 -0
  254. package/pp-core/bin/lib/roadmap.cjs +738 -0
  255. package/pp-core/bin/lib/runtime-artifact-conversion.cjs +1966 -0
  256. package/pp-core/bin/lib/runtime-artifact-layout.cjs +342 -0
  257. package/pp-core/bin/lib/runtime-config-adapter-registry.cjs +103 -0
  258. package/pp-core/bin/lib/runtime-homes.cjs +272 -0
  259. package/pp-core/bin/lib/runtime-hooks-surface.cjs +1460 -0
  260. package/pp-core/bin/lib/runtime-name-policy.cjs +97 -0
  261. package/pp-core/bin/lib/runtime-slash.cjs +137 -0
  262. package/pp-core/bin/lib/schema-detect.cjs +159 -0
  263. package/pp-core/bin/lib/secrets.cjs +34 -0
  264. package/pp-core/bin/lib/security.cjs +480 -0
  265. package/pp-core/bin/lib/semver-compare.cjs +42 -0
  266. package/pp-core/bin/lib/shell-command-projection.cjs +550 -0
  267. package/pp-core/bin/lib/state-command-router.cjs +172 -0
  268. package/pp-core/bin/lib/state-document.cjs +312 -0
  269. package/pp-core/bin/lib/state.cjs +2587 -0
  270. package/pp-core/bin/lib/surface.cjs +505 -0
  271. package/pp-core/bin/lib/task-command-router.cjs +85 -0
  272. package/pp-core/bin/lib/teams-status.cjs +74 -0
  273. package/pp-core/bin/lib/template.cjs +246 -0
  274. package/pp-core/bin/lib/uat-predicate.cjs +329 -0
  275. package/pp-core/bin/lib/uat.cjs +359 -0
  276. package/pp-core/bin/lib/ui-safety-gate.cjs +98 -0
  277. package/pp-core/bin/lib/update-context.cjs +221 -0
  278. package/pp-core/bin/lib/validate-command-router.cjs +91 -0
  279. package/pp-core/bin/lib/validate.cjs +122 -0
  280. package/pp-core/bin/lib/verification-command-router.cjs +31 -0
  281. package/pp-core/bin/lib/verification.cjs +196 -0
  282. package/pp-core/bin/lib/verify-command-router.cjs +44 -0
  283. package/pp-core/bin/lib/verify.cjs +1932 -0
  284. package/pp-core/bin/lib/workstream-inventory-builder.cjs +81 -0
  285. package/pp-core/bin/lib/workstream-inventory.cjs +147 -0
  286. package/pp-core/bin/lib/workstream-name-policy.cjs +91 -0
  287. package/pp-core/bin/lib/workstream.cjs +386 -0
  288. package/pp-core/bin/lib/worktree-base-ref.cjs +350 -0
  289. package/pp-core/bin/lib/worktree-safety.cjs +985 -0
  290. package/pp-core/bin/pp-tools.cjs +2239 -0
  291. package/pp-core/bin/pp_run +20 -0
  292. package/pp-core/bin/shared/config-defaults.manifest.json +97 -0
  293. package/pp-core/bin/shared/config-schema.manifest.json +176 -0
  294. package/pp-core/bin/shared/model-catalog.json +160 -0
  295. package/pp-core/bin/shared/runtime-aliases.manifest.json +79 -0
  296. package/pp-core/bin/verify-reapply-patches.cjs +400 -0
  297. package/pp-core/contexts/dev.md +21 -0
  298. package/pp-core/contexts/research.md +22 -0
  299. package/pp-core/contexts/review.md +23 -0
  300. package/pp-core/references/agent-contracts.md +79 -0
  301. package/pp-core/references/ai-evals.md +156 -0
  302. package/pp-core/references/ai-frameworks.md +186 -0
  303. package/pp-core/references/artifact-types.md +131 -0
  304. package/pp-core/references/autonomous-smart-discuss.md +277 -0
  305. package/pp-core/references/checkpoints.md +814 -0
  306. package/pp-core/references/common-bug-patterns.md +114 -0
  307. package/pp-core/references/context-budget.md +85 -0
  308. package/pp-core/references/continuation-format.md +253 -0
  309. package/pp-core/references/debugger-philosophy.md +76 -0
  310. package/pp-core/references/decimal-phase-calculation.md +64 -0
  311. package/pp-core/references/doc-conflict-engine.md +91 -0
  312. package/pp-core/references/domain-probes.md +125 -0
  313. package/pp-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +7 -0
  314. package/pp-core/references/edge-probe-fixtures/01-round-half-even/requirements.json +1 -0
  315. package/pp-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +8 -0
  316. package/pp-core/references/edge-probe-fixtures/02-merge-intervals/requirements.json +1 -0
  317. package/pp-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +7 -0
  318. package/pp-core/references/edge-probe-fixtures/03-truncate-graphemes/requirements.json +1 -0
  319. package/pp-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +7 -0
  320. package/pp-core/references/edge-probe-fixtures/04-money-rounding/requirements.json +1 -0
  321. package/pp-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +8 -0
  322. package/pp-core/references/edge-probe-fixtures/05-list-dedupe/requirements.json +1 -0
  323. package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +8 -0
  324. package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/requirements.json +1 -0
  325. package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/resolutions.json +4 -0
  326. package/pp-core/references/edge-probe.md +272 -0
  327. package/pp-core/references/execute-mvp-tdd.md +81 -0
  328. package/pp-core/references/executor-examples.md +110 -0
  329. package/pp-core/references/few-shot-examples/plan-checker.md +73 -0
  330. package/pp-core/references/few-shot-examples/verifier.md +109 -0
  331. package/pp-core/references/gate-prompts.md +100 -0
  332. package/pp-core/references/gates.md +70 -0
  333. package/pp-core/references/git-integration.md +298 -0
  334. package/pp-core/references/git-planning-commit.md +40 -0
  335. package/pp-core/references/ios-scaffold.md +123 -0
  336. package/pp-core/references/loop-hook-dispatch.md +61 -0
  337. package/pp-core/references/mandatory-initial-read.md +2 -0
  338. package/pp-core/references/model-profile-resolution.md +38 -0
  339. package/pp-core/references/model-profiles.md +245 -0
  340. package/pp-core/references/mvp-concepts.md +49 -0
  341. package/pp-core/references/phase-argument-parsing.md +61 -0
  342. package/pp-core/references/planner-antipatterns.md +176 -0
  343. package/pp-core/references/planner-chunked.md +49 -0
  344. package/pp-core/references/planner-gap-closure.md +62 -0
  345. package/pp-core/references/planner-graphify-auto-update.md +67 -0
  346. package/pp-core/references/planner-guidance.md +186 -0
  347. package/pp-core/references/planner-human-verify-mode.md +57 -0
  348. package/pp-core/references/planner-interface-context.md +62 -0
  349. package/pp-core/references/planner-load-graph-context.md +36 -0
  350. package/pp-core/references/planner-mvp-mode.md +53 -0
  351. package/pp-core/references/planner-reviews.md +42 -0
  352. package/pp-core/references/planner-revision.md +87 -0
  353. package/pp-core/references/planner-source-audit.md +73 -0
  354. package/pp-core/references/planning-config.md +477 -0
  355. package/pp-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json +14 -0
  356. package/pp-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json +4 -0
  357. package/pp-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json +32 -0
  358. package/pp-core/references/prohibition-probe.md +326 -0
  359. package/pp-core/references/project-skills-discovery.md +19 -0
  360. package/pp-core/references/questioning.md +162 -0
  361. package/pp-core/references/research-documentation-lookup.md +29 -0
  362. package/pp-core/references/research-philosophy.md +29 -0
  363. package/pp-core/references/research-verification-protocol.md +27 -0
  364. package/pp-core/references/revision-loop.md +97 -0
  365. package/pp-core/references/scout-codebase.md +51 -0
  366. package/pp-core/references/skeleton-template.md +48 -0
  367. package/pp-core/references/sketch-interactivity.md +41 -0
  368. package/pp-core/references/sketch-theme-system.md +94 -0
  369. package/pp-core/references/sketch-tooling.md +45 -0
  370. package/pp-core/references/sketch-variant-patterns.md +81 -0
  371. package/pp-core/references/spidr-splitting.md +69 -0
  372. package/pp-core/references/tdd.md +330 -0
  373. package/pp-core/references/thinking-models-debug.md +44 -0
  374. package/pp-core/references/thinking-models-execution.md +50 -0
  375. package/pp-core/references/thinking-models-planning.md +62 -0
  376. package/pp-core/references/thinking-models-research.md +50 -0
  377. package/pp-core/references/thinking-models-verification.md +55 -0
  378. package/pp-core/references/thinking-partner.md +96 -0
  379. package/pp-core/references/ui-brand.md +162 -0
  380. package/pp-core/references/universal-anti-patterns.md +63 -0
  381. package/pp-core/references/user-profiling.md +681 -0
  382. package/pp-core/references/user-story-template.md +58 -0
  383. package/pp-core/references/verification-overrides.md +227 -0
  384. package/pp-core/references/verification-patterns.md +612 -0
  385. package/pp-core/references/verify-mvp-mode.md +85 -0
  386. package/pp-core/references/workstream-flag.md +111 -0
  387. package/pp-core/references/worktree-branch-check.md +44 -0
  388. package/pp-core/references/worktree-path-safety.md +67 -0
  389. package/pp-core/templates/AI-SPEC.md +246 -0
  390. package/pp-core/templates/DEBUG.md +169 -0
  391. package/pp-core/templates/README.md +77 -0
  392. package/pp-core/templates/SECURITY.md +61 -0
  393. package/pp-core/templates/UAT.md +265 -0
  394. package/pp-core/templates/UI-SPEC.md +100 -0
  395. package/pp-core/templates/VALIDATION.md +76 -0
  396. package/pp-core/templates/claude-md.md +145 -0
  397. package/pp-core/templates/codebase/architecture.md +255 -0
  398. package/pp-core/templates/codebase/concerns.md +310 -0
  399. package/pp-core/templates/codebase/conventions.md +307 -0
  400. package/pp-core/templates/codebase/integrations.md +280 -0
  401. package/pp-core/templates/codebase/stack.md +186 -0
  402. package/pp-core/templates/codebase/structure.md +285 -0
  403. package/pp-core/templates/codebase/testing.md +480 -0
  404. package/pp-core/templates/config.json +62 -0
  405. package/pp-core/templates/context.md +352 -0
  406. package/pp-core/templates/continue-here.md +78 -0
  407. package/pp-core/templates/copilot-instructions.md +7 -0
  408. package/pp-core/templates/debug-subagent-prompt.md +91 -0
  409. package/pp-core/templates/dev-preferences.md +21 -0
  410. package/pp-core/templates/discovery.md +146 -0
  411. package/pp-core/templates/discussion-log.md +63 -0
  412. package/pp-core/templates/milestone-archive.md +123 -0
  413. package/pp-core/templates/milestone.md +115 -0
  414. package/pp-core/templates/phase-prompt.md +610 -0
  415. package/pp-core/templates/planner-subagent-prompt.md +117 -0
  416. package/pp-core/templates/project.md +203 -0
  417. package/pp-core/templates/requirements.md +231 -0
  418. package/pp-core/templates/research-project/ARCHITECTURE.md +204 -0
  419. package/pp-core/templates/research-project/FEATURES.md +147 -0
  420. package/pp-core/templates/research-project/PITFALLS.md +200 -0
  421. package/pp-core/templates/research-project/STACK.md +120 -0
  422. package/pp-core/templates/research-project/SUMMARY.md +170 -0
  423. package/pp-core/templates/research.md +592 -0
  424. package/pp-core/templates/retrospective.md +54 -0
  425. package/pp-core/templates/roadmap.md +202 -0
  426. package/pp-core/templates/spec.md +333 -0
  427. package/pp-core/templates/state.md +195 -0
  428. package/pp-core/templates/summary-complex.md +60 -0
  429. package/pp-core/templates/summary-minimal.md +42 -0
  430. package/pp-core/templates/summary-standard.md +49 -0
  431. package/pp-core/templates/summary.md +249 -0
  432. package/pp-core/templates/user-profile.md +146 -0
  433. package/pp-core/templates/user-setup.md +311 -0
  434. package/pp-core/templates/verification-report.md +335 -0
  435. package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -0
  436. package/pp-core/workflows/add-backlog.md +91 -0
  437. package/pp-core/workflows/add-phase.md +113 -0
  438. package/pp-core/workflows/add-tests.md +355 -0
  439. package/pp-core/workflows/add-todo.md +161 -0
  440. package/pp-core/workflows/ai-integration-phase.md +295 -0
  441. package/pp-core/workflows/analyze-dependencies.md +96 -0
  442. package/pp-core/workflows/audit-fix.md +178 -0
  443. package/pp-core/workflows/audit-milestone.md +362 -0
  444. package/pp-core/workflows/audit-uat.md +110 -0
  445. package/pp-core/workflows/autonomous.md +886 -0
  446. package/pp-core/workflows/check-todos.md +180 -0
  447. package/pp-core/workflows/cleanup.md +195 -0
  448. package/pp-core/workflows/code-review-fix.md +506 -0
  449. package/pp-core/workflows/code-review.md +696 -0
  450. package/pp-core/workflows/complete-milestone.md +857 -0
  451. package/pp-core/workflows/debug.md +237 -0
  452. package/pp-core/workflows/diagnose-issues.md +245 -0
  453. package/pp-core/workflows/discovery-phase.md +291 -0
  454. package/pp-core/workflows/discovery-sprint.md +1546 -0
  455. package/pp-core/workflows/discuss-phase/modes/advisor.md +176 -0
  456. package/pp-core/workflows/discuss-phase/modes/all.md +28 -0
  457. package/pp-core/workflows/discuss-phase/modes/analyze.md +44 -0
  458. package/pp-core/workflows/discuss-phase/modes/auto.md +57 -0
  459. package/pp-core/workflows/discuss-phase/modes/batch.md +52 -0
  460. package/pp-core/workflows/discuss-phase/modes/chain.md +98 -0
  461. package/pp-core/workflows/discuss-phase/modes/default.md +141 -0
  462. package/pp-core/workflows/discuss-phase/modes/power.md +44 -0
  463. package/pp-core/workflows/discuss-phase/modes/text.md +55 -0
  464. package/pp-core/workflows/discuss-phase/templates/checkpoint.json +18 -0
  465. package/pp-core/workflows/discuss-phase/templates/context.md +136 -0
  466. package/pp-core/workflows/discuss-phase/templates/discussion-log.md +50 -0
  467. package/pp-core/workflows/discuss-phase-assumptions.md +675 -0
  468. package/pp-core/workflows/discuss-phase-power.md +291 -0
  469. package/pp-core/workflows/discuss-phase.md +520 -0
  470. package/pp-core/workflows/do.md +111 -0
  471. package/pp-core/workflows/docs-update.md +1168 -0
  472. package/pp-core/workflows/edit-phase.md +295 -0
  473. package/pp-core/workflows/eval-review.md +159 -0
  474. package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +95 -0
  475. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  476. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +117 -0
  477. package/pp-core/workflows/execute-phase/steps/worktree-recovery-policy.md +9 -0
  478. package/pp-core/workflows/execute-phase.md +1707 -0
  479. package/pp-core/workflows/execute-plan.md +541 -0
  480. package/pp-core/workflows/explore.md +146 -0
  481. package/pp-core/workflows/extract-learnings.md +243 -0
  482. package/pp-core/workflows/fast.md +124 -0
  483. package/pp-core/workflows/forensics.md +279 -0
  484. package/pp-core/workflows/graduation.md +196 -0
  485. package/pp-core/workflows/health.md +224 -0
  486. package/pp-core/workflows/help/modes/brief.md +22 -0
  487. package/pp-core/workflows/help/modes/default.md +50 -0
  488. package/pp-core/workflows/help/modes/full.md +791 -0
  489. package/pp-core/workflows/help/modes/topic.md +74 -0
  490. package/pp-core/workflows/help.md +24 -0
  491. package/pp-core/workflows/import.md +256 -0
  492. package/pp-core/workflows/inbox.md +387 -0
  493. package/pp-core/workflows/ingest-docs.md +340 -0
  494. package/pp-core/workflows/insert-phase.md +152 -0
  495. package/pp-core/workflows/list-phase-assumptions.md +178 -0
  496. package/pp-core/workflows/list-workspaces.md +57 -0
  497. package/pp-core/workflows/manager.md +435 -0
  498. package/pp-core/workflows/map-codebase.md +444 -0
  499. package/pp-core/workflows/milestone-summary.md +224 -0
  500. package/pp-core/workflows/mvp-phase.md +222 -0
  501. package/pp-core/workflows/new-milestone.md +643 -0
  502. package/pp-core/workflows/new-project.md +1563 -0
  503. package/pp-core/workflows/new-workspace.md +240 -0
  504. package/pp-core/workflows/next.md +347 -0
  505. package/pp-core/workflows/node-repair.md +92 -0
  506. package/pp-core/workflows/note.md +158 -0
  507. package/pp-core/workflows/pause-work.md +250 -0
  508. package/pp-core/workflows/plan-milestone-gaps.md +281 -0
  509. package/pp-core/workflows/plan-phase.md +1755 -0
  510. package/pp-core/workflows/plan-review-convergence.md +369 -0
  511. package/pp-core/workflows/plant-seed.md +230 -0
  512. package/pp-core/workflows/pr-branch.md +159 -0
  513. package/pp-core/workflows/profile-user.md +455 -0
  514. package/pp-core/workflows/progress.md +756 -0
  515. package/pp-core/workflows/quick.md +1045 -0
  516. package/pp-core/workflows/reapply-patches.md +443 -0
  517. package/pp-core/workflows/remove-phase.md +156 -0
  518. package/pp-core/workflows/remove-workspace.md +108 -0
  519. package/pp-core/workflows/resume-project.md +348 -0
  520. package/pp-core/workflows/review.md +765 -0
  521. package/pp-core/workflows/scan.md +107 -0
  522. package/pp-core/workflows/secure-phase.md +184 -0
  523. package/pp-core/workflows/session-report.md +146 -0
  524. package/pp-core/workflows/settings-advanced.md +816 -0
  525. package/pp-core/workflows/settings-integrations.md +312 -0
  526. package/pp-core/workflows/settings.md +592 -0
  527. package/pp-core/workflows/ship.md +465 -0
  528. package/pp-core/workflows/sketch-wrap-up.md +286 -0
  529. package/pp-core/workflows/sketch.md +361 -0
  530. package/pp-core/workflows/spec-phase.md +495 -0
  531. package/pp-core/workflows/spike-wrap-up.md +307 -0
  532. package/pp-core/workflows/spike.md +453 -0
  533. package/pp-core/workflows/stats.md +80 -0
  534. package/pp-core/workflows/sync-skills.md +182 -0
  535. package/pp-core/workflows/thread.md +222 -0
  536. package/pp-core/workflows/transition.md +694 -0
  537. package/pp-core/workflows/ui-phase.md +328 -0
  538. package/pp-core/workflows/ui-review.md +193 -0
  539. package/pp-core/workflows/ultraplan-phase.md +199 -0
  540. package/pp-core/workflows/undo.md +314 -0
  541. package/pp-core/workflows/update.md +501 -0
  542. package/pp-core/workflows/validate-phase.md +183 -0
  543. package/pp-core/workflows/verify-phase.md +569 -0
  544. package/pp-core/workflows/verify-work.md +804 -0
  545. package/scripts/affected-tests-lib.cjs +554 -0
  546. package/scripts/audit-workflow-script-paths.cjs +73 -0
  547. package/scripts/base64-scan.sh +351 -0
  548. package/scripts/build-hooks.js +254 -0
  549. package/scripts/changeset/README.md +129 -0
  550. package/scripts/changeset/cli.cjs +597 -0
  551. package/scripts/changeset/github-release-notes.cjs +199 -0
  552. package/scripts/changeset/lint.cjs +147 -0
  553. package/scripts/changeset/new.cjs +151 -0
  554. package/scripts/changeset/parse.cjs +114 -0
  555. package/scripts/changeset/render.cjs +34 -0
  556. package/scripts/changeset/serialize.cjs +130 -0
  557. package/scripts/check-alias-drift.cjs +114 -0
  558. package/scripts/check-env.cjs +312 -0
  559. package/scripts/check-npm-integrity.cjs +215 -0
  560. package/scripts/ci-guard-runner.cjs +22 -0
  561. package/scripts/ci-prepare-test-scope.cjs +93 -0
  562. package/scripts/ci-rebase-check.cjs +86 -0
  563. package/scripts/ci-test-scope.cjs +441 -0
  564. package/scripts/command-contract-helpers.cjs +64 -0
  565. package/scripts/diff-touches-shipped-paths.cjs +149 -0
  566. package/scripts/fix-slash-commands.cjs +161 -0
  567. package/scripts/gen-capability-registry.cjs +2646 -0
  568. package/scripts/gen-commands-reference.cjs +139 -0
  569. package/scripts/gen-inventory-manifest.cjs +114 -0
  570. package/scripts/gen-loop-host-contract.cjs +526 -0
  571. package/scripts/gen-research-agents.cjs +276 -0
  572. package/scripts/generate-package-identity.cjs +134 -0
  573. package/scripts/issue-dedupe.cjs +278 -0
  574. package/scripts/issue-version-gate.cjs +140 -0
  575. package/scripts/lib/allowlist-ratchet.cjs +236 -0
  576. package/scripts/lib/cli-exit.cjs +56 -0
  577. package/scripts/lint-allow-test-rule-refs.allowlist.json +328 -0
  578. package/scripts/lint-allow-test-rule-refs.cjs +162 -0
  579. package/scripts/lint-command-contract.cjs +118 -0
  580. package/scripts/lint-descriptions.cjs +87 -0
  581. package/scripts/lint-docs-required.cjs +222 -0
  582. package/scripts/lint-legacy-dir-name.cjs +166 -0
  583. package/scripts/lint-package-identity-drift.cjs +141 -0
  584. package/scripts/lint-pr-check-project-dir.cjs +99 -0
  585. package/scripts/lint-regression-test-names.allowlist.json +269 -0
  586. package/scripts/lint-regression-test-names.cjs +117 -0
  587. package/scripts/lint-shell-command-projection-drift.cjs +62 -0
  588. package/scripts/lint-skill-deps.cjs +185 -0
  589. package/scripts/lint-test-file-count.allowlist.json +181 -0
  590. package/scripts/lint-test-file-count.cjs +246 -0
  591. package/scripts/lint-windows-test-portability.cjs +178 -0
  592. package/scripts/mutation-matrix.cjs +323 -0
  593. package/scripts/pr-target-policy.cjs +63 -0
  594. package/scripts/pr-template-policy.cjs +268 -0
  595. package/scripts/prompt-injection-scan.sh +207 -0
  596. package/scripts/registry-smoke.cjs +337 -0
  597. package/scripts/release-notes/discord-release-summary.cjs +373 -0
  598. package/scripts/release-notes/format-github-release-notes.cjs +261 -0
  599. package/scripts/release-tarball-smoke.cjs +763 -0
  600. package/scripts/research-profiles.cjs +149 -0
  601. package/scripts/run-affected-tests.cjs +7 -0
  602. package/scripts/run-tests.cjs +589 -0
  603. package/scripts/secret-scan-lint.sh +231 -0
  604. package/scripts/secret-scan.sh +358 -0
  605. package/scripts/setup-branch-protection.sh +234 -0
  606. package/scripts/strip-prose-atrefs.cjs +106 -0
  607. package/scripts/sync-manifest-versions.cjs +119 -0
  608. package/scripts/sync-next-version.cjs +133 -0
  609. package/scripts/sync-rulesets.sh +34 -0
  610. package/scripts/sync-runtime-launcher.cjs +425 -0
  611. package/scripts/test-failure-reasons.cjs +34 -0
  612. package/scripts/update-size-baseline.cjs +68 -0
  613. package/scripts/verify-npm-publish.cjs +240 -0
  614. package/scripts/verify-opengsd-sweep.cjs +288 -0
  615. package/scripts/verify-phase-02-rename.sh +258 -0
  616. package/scripts/verify-phase-03-rename.sh +376 -0
  617. package/scripts/workflow-policy.cjs +483 -0
  618. package/scripts/workflow-size.cjs +90 -0
@@ -0,0 +1,589 @@
1
+ #!/usr/bin/env node
2
+ // Cross-platform test runner — resolves test file globs via Node
3
+ // instead of relying on shell expansion (which fails on Windows PowerShell/cmd).
4
+ // Propagates NODE_V8_COVERAGE so c8 collects coverage from the child process.
5
+ //
6
+ // Suite filtering (issue #3597):
7
+ // node scripts/run-tests.cjs # default — runs ALL tests (backcompat)
8
+ // node scripts/run-tests.cjs --suite all # explicit "everything"
9
+ // node scripts/run-tests.cjs --suite unit # only files with no other suite marker
10
+ // node scripts/run-tests.cjs --suite security # *.security.test.cjs
11
+ // node scripts/run-tests.cjs --suite integration # *.integration.test.cjs
12
+ // node scripts/run-tests.cjs --suite install # *.install.test.cjs
13
+ // node scripts/run-tests.cjs --suite slow # *.slow.test.cjs
14
+ // node scripts/run-tests.cjs --files "a.test.cjs b.test.cjs"
15
+ // node scripts/run-tests.cjs --files-from /tmp/selected-tests.txt
16
+ // node scripts/run-tests.cjs --suite unit --shard 1/3 # shard 1 of 3 (#1212)
17
+ //
18
+ // Sharding (issue #1212): --shard <i>/<n> runs a deterministic, balanced
19
+ // round-robin slice of the SORTED selected file list (file index k → shard
20
+ // k % n). i is 1-based (1..n); n >= 1; n=1 is a pure no-op (all files). The
21
+ // CI windows full-test lane shards across N parallel runners so per-job
22
+ // wall-clock scales as O(total/N) and stops hitting the job time cap. Sharding
23
+ // composes with --suite (it slices the post-filter selection) and preserves
24
+ // the existing 28K argv chunking WITHIN each shard.
25
+ //
26
+ // Suite grouping convention: filename suffix marker before `.test.cjs`.
27
+ // A file named `foo.security.test.cjs` belongs to the `security` suite.
28
+ // A file named `foo.test.cjs` (no marker) belongs to the `unit` suite.
29
+ // See docs/TESTING-SUITES.md for full grouping policy.
30
+ 'use strict';
31
+
32
+ const { readdirSync } = require('fs');
33
+ const { join, basename } = require('path');
34
+ const { execFileSync } = require('child_process');
35
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
36
+
37
+ const SUITES = ['all', 'unit', 'integration', 'install', 'security', 'slow'];
38
+
39
+ // ADR-457 build-at-publish: pp-core/bin/lib/*.cjs is generated from
40
+ // src/*.cts and gitignored, so on a clean checkout (fresh CI, before any build)
41
+ // the artifact is absent — yet test files require it. This is the universal
42
+ // chokepoint every test path funnels through (test:unit, --files-from, direct
43
+ // invocation), so build the artifact here.
44
+ //
45
+ // Strategy (incremental + re-emit-on-missing, closes both #969 failure modes):
46
+ // 1. Run tsc incrementally (fast ~380ms no-op when sources unchanged).
47
+ // 2. Verify every src/*.cts (non-.d.cts) maps to a non-empty pp-core/bin/lib/*.cjs.
48
+ // 3. If any expected .cjs is missing or zero-bytes (persistent-mirror scenario:
49
+ // tsc no-ops because tsbuildinfo looks current even though the file was deleted),
50
+ // delete the tsbuildinfo and run tsc ONCE MORE (clean re-emit), then re-verify.
51
+ //
52
+ // Common case: fast incremental no-op. Stale/deleted-output case: detected by
53
+ // the cheap existsSync loop and force-rebuilt. Paths resolve from __dirname so
54
+ // it works regardless of GSD_TEST_DIR / temp-dir cwd.
55
+ function ensureBuiltArtifacts(overrides = {}) {
56
+ const { existsSync, readdirSync, statSync, unlinkSync } = require('fs');
57
+ const root = overrides.root || join(__dirname, '..');
58
+ const srcDir = overrides.srcDir || join(root, 'src');
59
+ const outDir = overrides.outDir || join(root, 'pp-core', 'bin', 'lib');
60
+ const tsBuildInfoPath = overrides.tsBuildInfoPath || join(root, 'tsconfig.build.tsbuildinfo');
61
+ const tsconfigPath = overrides.tsconfigPath || join(root, 'tsconfig.build.json');
62
+ const tscBin = require.resolve('typescript/bin/tsc');
63
+ const tscArgs = [tscBin, '-p', tsconfigPath];
64
+
65
+ // Build the 1:1 map of expected output paths from src/*.cts sources.
66
+ // Excludes *.d.cts (declaration-only files that produce no output).
67
+ // Handles subdirectories (e.g. src/installer-migrations/*.cts → pp-core/bin/lib/installer-migrations/*.cjs).
68
+ function gatherExpectedOutputs() {
69
+ const expected = [];
70
+ function scan(dir, relBase) {
71
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
72
+ if (entry.isDirectory()) {
73
+ scan(join(dir, entry.name), relBase ? `${relBase}/${entry.name}` : entry.name);
74
+ } else if (entry.name.endsWith('.cts') && !entry.name.endsWith('.d.cts')) {
75
+ const stem = entry.name.slice(0, -'.cts'.length);
76
+ const rel = relBase ? `${relBase}/${stem}.cjs` : `${stem}.cjs`;
77
+ expected.push(join(outDir, rel));
78
+ }
79
+ }
80
+ }
81
+ scan(srcDir, '');
82
+ return expected;
83
+ }
84
+
85
+ function checkMissingOutputs(expectedPaths) {
86
+ return expectedPaths.filter(p => !existsSync(p) || statSync(p).size === 0);
87
+ }
88
+
89
+ // #996 placed the tsbuildinfo inside gsd-core/bin/ (a copied/shipped tree), which
90
+ // raced install-test copies. It now lives at the repo root. Best-effort purge any
91
+ // stale bin-local copy so persistent workspaces/mirrors self-heal (no-op on a temp
92
+ // override root or a clean checkout).
93
+ const legacyTsBuildInfo = join(root, 'pp-core', 'bin', 'tsconfig.build.tsbuildinfo');
94
+ try { if (existsSync(legacyTsBuildInfo)) unlinkSync(legacyTsBuildInfo); } catch { /* best-effort */ }
95
+
96
+ // Step 1: incremental build (fast no-op when sources unchanged).
97
+ execFileSync(process.execPath, tscArgs, { cwd: root, stdio: 'inherit' });
98
+
99
+ // Step 2: verify expected outputs.
100
+ const expected = gatherExpectedOutputs();
101
+ const missing = checkMissingOutputs(expected);
102
+
103
+ // Step 3: if any output is missing/zero-bytes, force a clean re-emit.
104
+ // This handles the persistent-mirror case where tsc's incremental no-op left
105
+ // a deleted .cjs unregenerated (tsbuildinfo recorded it as up-to-date).
106
+ if (missing.length > 0) {
107
+ if (existsSync(tsBuildInfoPath)) {
108
+ unlinkSync(tsBuildInfoPath);
109
+ }
110
+ execFileSync(process.execPath, tscArgs, { cwd: root, stdio: 'inherit' });
111
+ // Re-verify after clean re-emit; surface any remaining gaps loudly.
112
+ const stillMissing = checkMissingOutputs(expected);
113
+ if (stillMissing.length > 0) {
114
+ const names = stillMissing.map(p => require('path').basename(p)).join(', ');
115
+ throw new Error(
116
+ `ensureBuiltArtifacts: tsc clean re-emit still missing outputs: ${names}. ` +
117
+ `Check src/ for compilation errors.`
118
+ );
119
+ }
120
+ }
121
+ }
122
+ const MARKED_SUITES = ['integration', 'install', 'security', 'slow'];
123
+
124
+ // Recursively collect *.test.cjs files under dir, returning paths relative to dir.
125
+ // Skips node_modules to avoid accidentally picking up decoy files.
126
+ function walkTestFiles(dir, relBase) {
127
+ const results = [];
128
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
129
+ if (entry.isDirectory()) {
130
+ if (entry.name === 'node_modules') continue;
131
+ results.push(...walkTestFiles(join(dir, entry.name), relBase ? `${relBase}/${entry.name}` : entry.name));
132
+ } else if (entry.name.endsWith('.test.cjs')) {
133
+ results.push(relBase ? `${relBase}/${entry.name}` : entry.name);
134
+ }
135
+ }
136
+ return results;
137
+ }
138
+
139
+ // Parse a `--shard i/n` value into { index, total } or { error }.
140
+ // i is 1-based and must satisfy 1 <= i <= n; n must be >= 1. Both parts must be
141
+ // plain non-negative integers (no decimals, signs, or surrounding whitespace).
142
+ // `n=1` is the pure no-op (every file). This is the strict-input boundary
143
+ // (Postel's Law: be strict in what a CLI flag accepts so a typo fails loudly
144
+ // rather than silently running the wrong slice of the suite).
145
+ function parseShardArg(value) {
146
+ if (typeof value !== 'string') {
147
+ return { error: `--shard requires a value of the form i/n` };
148
+ }
149
+ const m = /^(\d+)\/(\d+)$/.exec(value);
150
+ if (!m) {
151
+ return { error: `--shard value "${value}" must be of the form i/n (e.g. 1/3)` };
152
+ }
153
+ const index = Number(m[1]);
154
+ const total = Number(m[2]);
155
+ if (!Number.isInteger(total) || total < 1) {
156
+ return { error: `--shard total n must be an integer >= 1, got "${m[2]}"` };
157
+ }
158
+ if (!Number.isInteger(index) || index < 1 || index > total) {
159
+ return { error: `--shard index i must be an integer in 1..${total}, got "${m[1]}"` };
160
+ }
161
+ return { index, total };
162
+ }
163
+
164
+ // Deterministic, balanced round-robin partition of an ALREADY-SORTED file list.
165
+ // Shard `index` (1-based) receives every file whose position k in the sorted
166
+ // list satisfies k % total === index - 1. Round-robin (not contiguous blocks)
167
+ // spreads duration variance across shards and guarantees shard sizes differ by
168
+ // at most 1. Selection keys off array INDEX, never off the path string, so the
169
+ // partition is byte-identical across Windows/macOS/Linux as long as the caller
170
+ // sorts the list with the same (locale-independent) comparator. `total=1`
171
+ // returns the input unchanged (pure no-op). A shard with no files (total >
172
+ // file count) returns [] and is a legitimate result, not an error.
173
+ function selectShard(sortedFiles, { index, total }) {
174
+ if (total === 1) return sortedFiles;
175
+ return sortedFiles.filter((_, k) => k % total === index - 1);
176
+ }
177
+
178
+ function parseArgs(argv) {
179
+ let suite = null;
180
+ let seen = false;
181
+ let files = null;
182
+ let filesFrom = null;
183
+ let shard = null;
184
+ let shardSeen = false;
185
+ for (let i = 0; i < argv.length; i++) {
186
+ const a = argv[i];
187
+ if (a === '--shard' || a.startsWith('--shard=')) {
188
+ if (shardSeen) {
189
+ return { error: 'duplicate --shard flag' };
190
+ }
191
+ shardSeen = true;
192
+ let v;
193
+ if (a === '--shard') {
194
+ v = argv[i + 1];
195
+ if (v === undefined || (typeof v === 'string' && v.startsWith('--'))) {
196
+ return { error: '--shard requires a value of the form i/n' };
197
+ }
198
+ i++;
199
+ } else {
200
+ v = a.slice('--shard='.length);
201
+ }
202
+ const parsed = parseShardArg(v);
203
+ if (parsed.error) {
204
+ return { error: parsed.error };
205
+ }
206
+ shard = parsed;
207
+ } else if (a === '--suite') {
208
+ if (seen) {
209
+ return { error: 'duplicate --suite flag' };
210
+ }
211
+ seen = true;
212
+ const v = argv[i + 1];
213
+ if (!v || v.startsWith('--')) {
214
+ return { error: '--suite requires a value' };
215
+ }
216
+ suite = v;
217
+ i++;
218
+ } else if (a.startsWith('--suite=')) {
219
+ if (seen) {
220
+ return { error: 'duplicate --suite flag' };
221
+ }
222
+ seen = true;
223
+ suite = a.slice('--suite='.length);
224
+ if (!suite) {
225
+ return { error: '--suite requires a value' };
226
+ }
227
+ } else if (a === '--files') {
228
+ if (files !== null) {
229
+ return { error: 'duplicate --files flag' };
230
+ }
231
+ const v = argv[i + 1];
232
+ if (!v || v.startsWith('--')) {
233
+ return { error: '--files requires a value' };
234
+ }
235
+ files = v;
236
+ i++;
237
+ } else if (a.startsWith('--files=')) {
238
+ if (files !== null) {
239
+ return { error: 'duplicate --files flag' };
240
+ }
241
+ files = a.slice('--files='.length);
242
+ if (!files) {
243
+ return { error: '--files requires a value' };
244
+ }
245
+ } else if (a === '--files-from') {
246
+ if (filesFrom !== null) {
247
+ return { error: 'duplicate --files-from flag' };
248
+ }
249
+ const v = argv[i + 1];
250
+ if (!v || v.startsWith('--')) {
251
+ return { error: '--files-from requires a value' };
252
+ }
253
+ filesFrom = v;
254
+ i++;
255
+ } else if (a.startsWith('--files-from=')) {
256
+ if (filesFrom !== null) {
257
+ return { error: 'duplicate --files-from flag' };
258
+ }
259
+ filesFrom = a.slice('--files-from='.length);
260
+ if (!filesFrom) {
261
+ return { error: '--files-from requires a value' };
262
+ }
263
+ } else {
264
+ return { error: `unknown argument: ${a}` };
265
+ }
266
+ }
267
+ if (files !== null && filesFrom !== null) {
268
+ return { error: '--files and --files-from cannot be combined' };
269
+ }
270
+ return { suite, files, filesFrom, shard };
271
+ }
272
+
273
+ // Return the marked suite name embedded in a filename, or null if it's unmarked.
274
+ // foo.security.test.cjs -> "security"
275
+ // foo.test.cjs -> null (unit)
276
+ // Accepts either a bare filename or a relative subdir path; classification is
277
+ // based on the basename only so subdir paths classify identically to root files.
278
+ function suiteOf(filename) {
279
+ const name = basename(filename);
280
+ if (!name.endsWith('.test.cjs')) return null;
281
+ const base = name.slice(0, -'.test.cjs'.length);
282
+ const lastDot = base.lastIndexOf('.');
283
+ if (lastDot === -1) return null;
284
+ const marker = base.slice(lastDot + 1);
285
+ return MARKED_SUITES.includes(marker) ? marker : null;
286
+ }
287
+
288
+ function selectFiles(allFiles, suite) {
289
+ if (suite === null || suite === 'all') {
290
+ return allFiles;
291
+ }
292
+ if (suite === 'unit') {
293
+ return allFiles.filter(f => suiteOf(f) === null);
294
+ }
295
+ return allFiles.filter(f => suiteOf(f) === suite);
296
+ }
297
+
298
+ function splitFileList(value) {
299
+ if (!value) return [];
300
+ return value
301
+ .split(/[,\s]+/)
302
+ .map(v => v.trim())
303
+ .filter(Boolean)
304
+ .map(v => v.replace(/\\/g, '/')) // normalize Windows backslashes
305
+ .map(v => v.replace(/^tests\//, ''));
306
+ }
307
+
308
+ function selectExplicitFiles(allFiles, filesValue, filesFrom) {
309
+ const fs = require('fs');
310
+ const requested = filesFrom
311
+ ? splitFileList(fs.readFileSync(filesFrom, 'utf8'))
312
+ : splitFileList(filesValue);
313
+ const available = new Set(allFiles);
314
+
315
+ // Build a basename -> [relpath, ...] index for bare-basename resolution.
316
+ // A bare basename (no directory separator) may match exactly one subdir file.
317
+ const basenameIndex = new Map();
318
+ for (const f of allFiles) {
319
+ const b = basename(f);
320
+ if (!basenameIndex.has(b)) basenameIndex.set(b, []);
321
+ basenameIndex.get(b).push(f);
322
+ }
323
+
324
+ const selected = [];
325
+ const missing = [];
326
+ const errors = [];
327
+ for (const file of requested) {
328
+ // If the token is a bare suite name (e.g. "unit" written by ci-test-scope
329
+ // as the #408 fallback sentinel), delegate to the existing suite resolver
330
+ // rather than treating it as a filename. This prevents the
331
+ // "requested test file(s) not found: unit" crash (#641).
332
+ if (SUITES.includes(file)) {
333
+ for (const f of selectFiles(allFiles, file)) {
334
+ selected.push(f);
335
+ }
336
+ } else if (available.has(file)) {
337
+ // Exact relpath match (e.g. "installer-migrations/001-legacy-orphan-files.test.cjs").
338
+ selected.push(file);
339
+ } else if (!file.includes('/')) {
340
+ // Bare basename (no directory separator): resolve via index.
341
+ const candidates = basenameIndex.get(file);
342
+ if (!candidates || candidates.length === 0) {
343
+ missing.push(file);
344
+ } else if (candidates.length > 1) {
345
+ errors.push(
346
+ `ambiguous basename "${file}" matches multiple files: ${candidates.join(', ')} — pass the subdir path instead`,
347
+ );
348
+ } else {
349
+ selected.push(candidates[0]);
350
+ }
351
+ } else {
352
+ missing.push(file);
353
+ }
354
+ }
355
+ if (errors.length > 0) {
356
+ return { error: errors.join('; ') };
357
+ }
358
+ if (missing.length > 0) {
359
+ return {
360
+ error: `requested test file(s) not found: ${missing.join(', ')}`,
361
+ };
362
+ }
363
+ return { files: [...new Set(selected)] };
364
+ }
365
+
366
+ function main() {
367
+ const args = process.argv.slice(2);
368
+ const parsed = parseArgs(args);
369
+ if (parsed.error) {
370
+ console.error(`run-tests: ${parsed.error}`);
371
+ console.error(`Valid suites: ${SUITES.join(', ')}`);
372
+ throw new ExitError(2);
373
+ }
374
+ const suite = parsed.suite;
375
+ if (suite !== null && !SUITES.includes(suite)) {
376
+ console.error(`run-tests: unknown suite "${suite}"`);
377
+ console.error(`Valid suites: ${SUITES.join(', ')}`);
378
+ throw new ExitError(2);
379
+ }
380
+
381
+ const testDir = process.env.GSD_TEST_DIR
382
+ ? process.env.GSD_TEST_DIR
383
+ : join(__dirname, '..', 'tests');
384
+
385
+ const allFiles = walkTestFiles(testDir, '').sort();
386
+
387
+ if (allFiles.length === 0) {
388
+ console.error(`No test files found in ${testDir}`);
389
+ throw new ExitError(1);
390
+ }
391
+
392
+ const usingExplicitFiles = parsed.files !== null || parsed.filesFrom !== null;
393
+ let selectedNames;
394
+ if (usingExplicitFiles) {
395
+ const explicit = selectExplicitFiles(allFiles, parsed.files, parsed.filesFrom);
396
+ if (explicit.error) {
397
+ console.error(`run-tests: ${explicit.error}`);
398
+ throw new ExitError(2);
399
+ }
400
+ selectedNames = explicit.files;
401
+ } else {
402
+ selectedNames = selectFiles(allFiles, suite);
403
+ }
404
+
405
+ // Shard partitioning (#1212): when --shard i/n is given, keep only this
406
+ // shard's deterministic round-robin slice of the selected list. Applied
407
+ // AFTER suite/explicit selection so it composes with --suite (each shard
408
+ // runs i/n of the post-filter selection).
409
+ //
410
+ // The partition keys off array index, so the slice is only reproducible if
411
+ // the input is in a stable order. --suite/default selections are already
412
+ // sorted (allFiles came from walkTestFiles(...).sort() and selectFiles
413
+ // preserves that order), but --files/--files-from preserve REQUEST order.
414
+ // Sort here so --shard is deterministic regardless of how the selection was
415
+ // produced — the runner's documented contract is a sorted partition.
416
+ //
417
+ // emptyBeforeShard distinguishes "this shard legitimately got zero files
418
+ // from a non-empty list" (total > file count — a valid no-op) from "the
419
+ // selection was already empty before sharding" (a genuinely empty suite,
420
+ // which must still hit the discovery hard-error below — Codex #1212 review).
421
+ const usingShard = parsed.shard !== null;
422
+ let emptyBeforeShard = false;
423
+ if (usingShard) {
424
+ emptyBeforeShard = selectedNames.length === 0;
425
+ selectedNames = selectShard([...selectedNames].sort(), parsed.shard);
426
+ }
427
+
428
+ const selected = selectedNames.map(f => join(testDir, f));
429
+
430
+ if (selected.length === 0) {
431
+ // A legitimately-empty shard: --shard was given, the pre-shard selection
432
+ // had files, but this shard index drew zero (total > file count). Exit 0.
433
+ const legitimatelyEmptyShard = usingShard && !emptyBeforeShard;
434
+ if (usingExplicitFiles || legitimatelyEmptyShard) {
435
+ // Empty file list from --files/--files-from (e.g. CI passes an empty
436
+ // .ci-selected-tests.txt on docs-only/inert PRs) OR a legitimately-empty
437
+ // shard: both are expected. Exit 0 silently rather than taking the
438
+ // "discovery broken" hard-error path below. An EMPTY suite that was
439
+ // empty BEFORE sharding falls through to the hard error so a broken
440
+ // --suite filter is still caught even with --shard present.
441
+ console.error(`run-tests: no tests in suite "${suite || 'all'}"`);
442
+ return 0;
443
+ }
444
+ // Empty suite/default run: this means discovery or the suite filter is broken.
445
+ // Allow GSD_ALLOW_EMPTY_SUITE=1 as an escape hatch (downgrades to a warning).
446
+ if (process.env.GSD_ALLOW_EMPTY_SUITE === '1') {
447
+ console.error(`run-tests: WARNING: 0 test files selected for suite "${suite || 'all'}" — discovery or suite filter may be broken (GSD_ALLOW_EMPTY_SUITE=1 suppressed the error)`);
448
+ return 0;
449
+ }
450
+ console.error(`run-tests: ERROR: 0 test files selected for suite "${suite || 'all'}" — discovery or suite filter is broken`);
451
+ throw new ExitError(1);
452
+ }
453
+
454
+ // Build the gitignored bin/lib artifact if absent, before any test requires it.
455
+ ensureBuiltArtifacts();
456
+
457
+ // Hermeticity: in-process tests resolve `.planning` via planningDir(cwd), which
458
+ // honours GSD_PROJECT/GSD_WORKSTREAM. A developer shell inside a GSD workstream
459
+ // exports GSD_WORKSTREAM, which would redirect fixture STATE.md reads away from
460
+ // each <tmp>/.planning and silently diverge from the clean CI/Docker env. Strip
461
+ // them so the local runner matches CI; tests that need them set them explicitly.
462
+ delete process.env.GSD_PROJECT;
463
+ delete process.env.GSD_WORKSTREAM;
464
+ delete process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
465
+
466
+ // Log selected files to stderr for CI / harness-test visibility.
467
+ // node:test default reporter doesn't echo filenames, so this gives
468
+ // operators a single stable line they can grep.
469
+ console.error(
470
+ `run-tests: suite="${suite || 'all'}" files=${selected.length}: ${selected
471
+ .map(f => f.split(/[\\/]/).pop())
472
+ .join(' ')}`,
473
+ );
474
+
475
+ // Default concurrency: 4 on Linux/macOS, 2 on Windows.
476
+ //
477
+ // Windows has significantly higher per-subprocess overhead than Linux/macOS:
478
+ // - Windows Defender scans each spawned process on first execution, adding
479
+ // latency proportional to the number of concurrent spawns.
480
+ // - NTFS has higher file-system latency under concurrent access compared to
481
+ // ext4/APFS, which amplifies contention when multiple test chunks run in
482
+ // parallel and all read/write the same fixture directories.
483
+ // Reducing to 2 halves the peak concurrent subprocess count on Windows and
484
+ // keeps per-chunk wall-clock time well within the 20m CI job cap.
485
+ //
486
+ // Operator override via TEST_CONCURRENCY env var for local debugging.
487
+ const defaultConcurrency = process.platform === 'win32' ? 2 : 4;
488
+ const concurrency = process.env.TEST_CONCURRENCY
489
+ ? `--test-concurrency=${process.env.TEST_CONCURRENCY}`
490
+ : `--test-concurrency=${defaultConcurrency}`;
491
+
492
+ // Windows `CreateProcess` caps the full command line at 32,767 chars
493
+ // (lpCommandLine). With 500+ test paths the spawn fails instantly with no
494
+ // test output. Linux/macOS allow ~2 MB (ARG_MAX) so unchunked spawns are
495
+ // fine there. Split into chunks sized for the tightest target so behavior
496
+ // is identical across platforms. (#3597)
497
+ // Operator override (also used by tests to force chunking with short paths).
498
+ const MAX_CMDLINE_CHARS = process.env.RUN_TESTS_MAX_CMDLINE_CHARS
499
+ ? Number(process.env.RUN_TESTS_MAX_CMDLINE_CHARS)
500
+ : 28000; // headroom below the 32,767 Windows ceiling
501
+ const MAX_FILES_PER_CHUNK = process.env.RUN_TESTS_MAX_FILES_PER_CHUNK
502
+ ? Number(process.env.RUN_TESTS_MAX_FILES_PER_CHUNK)
503
+ : 180;
504
+
505
+ // node:test does not exit until the event loop drains. A unit test that leaks
506
+ // an open handle (un-terminated Worker, un-killed child_process, ref'd timer)
507
+ // makes a chunk's `node --test` child hang ~150s on Windows AFTER its last test
508
+ // prints; two such stalls push the windows full lane past its 20m cap and the
509
+ // job is CANCELLED with no failed step — a false-negative gate (#1051, recurrence
510
+ // of #869). --test-force-exit (Node >=22; engines requires >=22.0.0) exits the
511
+ // runner once all tests finish regardless of lingering handles. The leaking
512
+ // tests are also fixed at the source; this is the defensive backstop.
513
+ // RUN_TESTS_NO_FORCE_EXIT=1 disables it (used by the harness regression test to
514
+ // observe the pre-fix hang).
515
+ const nodeMajor = Number(process.versions.node.split('.')[0]);
516
+ const forceExit = nodeMajor >= 22 && !process.env.RUN_TESTS_NO_FORCE_EXIT;
517
+
518
+ const FIXED_OVERHEAD = process.execPath.length + '--test'.length + concurrency.length + (forceExit ? '--test-force-exit'.length + 1 : 0) + 8;
519
+ const chunks = [];
520
+ let current = [];
521
+ let currentLen = FIXED_OVERHEAD;
522
+ for (const file of selected) {
523
+ const add = file.length + 1; // +1 for the inter-arg separator
524
+ if (
525
+ current.length > 0 &&
526
+ (currentLen + add > MAX_CMDLINE_CHARS || current.length >= MAX_FILES_PER_CHUNK)
527
+ ) {
528
+ chunks.push(current);
529
+ current = [];
530
+ currentLen = FIXED_OVERHEAD;
531
+ }
532
+ current.push(file);
533
+ currentLen += add;
534
+ }
535
+ if (current.length > 0) chunks.push(current);
536
+
537
+ // A chunk that still hangs (a leak the backstop somehow misses, or a wedged
538
+ // subprocess) must fail loudly rather than silently burn the job's wall-clock
539
+ // budget until the CI runner cancels the whole job. Default 10 min per chunk:
540
+ // well above a healthy chunk (~4-5 min on the windows lane) but below the 20m
541
+ // job cap. Operator/test override via RUN_TESTS_CHUNK_TIMEOUT_MS.
542
+ const chunkTimeoutMs = process.env.RUN_TESTS_CHUNK_TIMEOUT_MS
543
+ ? Number(process.env.RUN_TESTS_CHUNK_TIMEOUT_MS)
544
+ : 600000;
545
+
546
+ let firstFailureExit = 0;
547
+ for (let i = 0; i < chunks.length; i++) {
548
+ if (chunks.length > 1) {
549
+ console.error(`run-tests: chunk ${i + 1}/${chunks.length} — ${chunks[i].length} files`);
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
+ },
560
+ );
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;
580
+ }
581
+ }
582
+ if (firstFailureExit !== 0) return firstFailureExit;
583
+ }
584
+
585
+ if (require.main === module) {
586
+ runMain(main);
587
+ }
588
+
589
+ module.exports = { suiteOf, ensureBuiltArtifacts, parseShardArg, selectShard };