@pareto4/pp-core 1.11.1 → 1.12.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 (257) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +31 -0
  3. package/agents/pp-code-fixer.md +1 -1
  4. package/agents/pp-debug-session-manager.md +1 -1
  5. package/agents/pp-debugger.md +1 -1
  6. package/agents/pp-executor.md +20 -10
  7. package/agents/pp-intel-updater.md +1 -1
  8. package/agents/pp-phase-researcher.md +1 -1
  9. package/agents/pp-plan-checker.md +5 -5
  10. package/agents/pp-planner.md +10 -24
  11. package/agents/pp-project-researcher.md +1 -1
  12. package/agents/pp-research-synthesizer.md +1 -1
  13. package/agents/pp-ui-researcher.md +1 -1
  14. package/agents/pp-verifier.md +8 -4
  15. package/bin/install.js +429 -82
  16. package/commands/pp-autonomous.md +8 -6
  17. package/commands/pp-discovery-to-build.md +1 -1
  18. package/commands/pp-discovery.md +3 -2
  19. package/commands/pp-discuss-phase.md +2 -2
  20. package/commands/pp-execute-phase.md +3 -1
  21. package/commands/pp-graphify.md +5 -5
  22. package/commands/pp-import.md +1 -1
  23. package/commands/pp-map-codebase.md +1 -1
  24. package/commands/pp-ns-project.md +2 -3
  25. package/commands/pp-ns-workflow.md +1 -1
  26. package/commands/pp-plan-phase.md +2 -1
  27. package/commands/pp-verify-work.md +2 -1
  28. package/gemini-extension.json +1 -1
  29. package/hooks/dist/pp-cursor-session-start.js +2 -2
  30. package/hooks/dist/pp-session-state.sh +1 -1
  31. package/hooks/dist/pp-statusline.js +21 -2
  32. package/hooks/pp-cursor-session-start.js +2 -2
  33. package/hooks/pp-session-state.sh +1 -1
  34. package/hooks/pp-statusline.js +21 -2
  35. package/package.json +18 -5
  36. package/pareto-skills/pp-project-setup/SKILL.md +5 -63
  37. package/pp-core/bin/detect-build-commands.sh +182 -0
  38. package/pp-core/bin/lib/capability-activation.cjs +19 -9
  39. package/pp-core/bin/lib/capability-registry.cjs +65 -7
  40. package/pp-core/bin/lib/check-command-router.cjs +79 -38
  41. package/pp-core/bin/lib/clusters.cjs +2 -3
  42. package/pp-core/bin/lib/command-aliases.cjs +36 -0
  43. package/pp-core/bin/lib/command-arg-projection.cjs +99 -1
  44. package/pp-core/bin/lib/commands.cjs +21 -6
  45. package/pp-core/bin/lib/compose-bindings.cjs +70 -6
  46. package/pp-core/bin/lib/config-loader.cjs +151 -8
  47. package/pp-core/bin/lib/config-schema.cjs +16 -0
  48. package/pp-core/bin/lib/config.cjs +142 -27
  49. package/pp-core/bin/lib/core-utils.cjs +43 -7
  50. package/pp-core/bin/lib/discovery-input-check.cjs +218 -0
  51. package/pp-core/bin/lib/discovery-rebaseline.cjs +1372 -0
  52. package/pp-core/bin/lib/discovery-research-track.cjs +979 -0
  53. package/pp-core/bin/lib/discovery-rollup.cjs +476 -0
  54. package/pp-core/bin/lib/discovery-screens.cjs +1995 -0
  55. package/pp-core/bin/lib/discovery-sweep.cjs +390 -0
  56. package/pp-core/bin/lib/discovery-table.cjs +98 -0
  57. package/pp-core/bin/lib/dispatch-plan.cjs +472 -0
  58. package/pp-core/bin/lib/frontmatter.cjs +17 -2
  59. package/pp-core/bin/lib/init-command-router.cjs +8 -5
  60. package/pp-core/bin/lib/init-contract.cjs +93 -4
  61. package/pp-core/bin/lib/init.cjs +124 -134
  62. package/pp-core/bin/lib/install-profiles.cjs +5 -0
  63. package/pp-core/bin/lib/install-run.cjs +536 -0
  64. package/pp-core/bin/lib/io.cjs +15 -0
  65. package/pp-core/bin/lib/loop-resolver.cjs +40 -2
  66. package/pp-core/bin/lib/milestone.cjs +345 -48
  67. package/pp-core/bin/lib/phase-command-router.cjs +23 -1
  68. package/pp-core/bin/lib/phase-locator.cjs +15 -6
  69. package/pp-core/bin/lib/phase-scope.cjs +91 -0
  70. package/pp-core/bin/lib/phase-tier.cjs +467 -0
  71. package/pp-core/bin/lib/phase.cjs +247 -21
  72. package/pp-core/bin/lib/phases-command-router.cjs +2 -0
  73. package/pp-core/bin/lib/plan-graph.cjs +368 -0
  74. package/pp-core/bin/lib/planning-workspace.cjs +53 -0
  75. package/pp-core/bin/lib/platform-bindings.cjs +32 -2
  76. package/pp-core/bin/lib/profile-output.cjs +11 -3
  77. package/pp-core/bin/lib/prohibition-enforcement.cjs +112 -0
  78. package/pp-core/bin/lib/roadmap-parser.cjs +366 -99
  79. package/pp-core/bin/lib/roadmap.cjs +141 -39
  80. package/pp-core/bin/lib/runtime-hooks-surface.cjs +1 -1
  81. package/pp-core/bin/lib/shell-command-projection.cjs +24 -0
  82. package/pp-core/bin/lib/state-command-router.cjs +82 -13
  83. package/pp-core/bin/lib/state-document.cjs +124 -7
  84. package/pp-core/bin/lib/state.cjs +784 -257
  85. package/pp-core/bin/lib/template.cjs +22 -6
  86. package/pp-core/bin/lib/vault-command-router.cjs +57 -6
  87. package/pp-core/bin/lib/vault-practices.cjs +6 -36
  88. package/pp-core/bin/lib/vault-write-back.cjs +20 -4
  89. package/pp-core/bin/lib/vault.cjs +592 -23
  90. package/pp-core/bin/lib/verify.cjs +53 -31
  91. package/pp-core/bin/lib/workstream.cjs +1 -1
  92. package/pp-core/bin/lib/worktree-safety.cjs +26 -15
  93. package/pp-core/bin/portable-timeout.sh +89 -0
  94. package/pp-core/bin/pp-tools.cjs +149 -9
  95. package/pp-core/bin/shared/config-defaults.manifest.json +7 -0
  96. package/pp-core/bin/shared/config-schema.manifest.json +3 -0
  97. package/pp-core/references/autonomous-best-effort.md +461 -0
  98. package/pp-core/references/autonomous-lifecycle.md +146 -0
  99. package/pp-core/references/autonomous-smart-discuss.md +16 -12
  100. package/pp-core/references/checkpoints.md +1 -1
  101. package/pp-core/references/discovery-id-conventions.md +202 -0
  102. package/pp-core/references/discovery-rebaseline.md +254 -0
  103. package/pp-core/references/discovery-research-track.md +283 -0
  104. package/pp-core/references/discovery-traceability.md +400 -0
  105. package/pp-core/references/loop-hook-dispatch.md +14 -0
  106. package/pp-core/references/mandatory-initial-read.md +1 -1
  107. package/pp-core/references/planner-gap-closure.md +1 -0
  108. package/pp-core/references/planner-human-verify-mode.md +1 -1
  109. package/pp-core/references/planner-load-graph-context.md +1 -1
  110. package/pp-core/references/planner-plan-graph.md +66 -0
  111. package/pp-core/references/planner-tdd.md +1 -0
  112. package/pp-core/references/planning-config.md +8 -0
  113. package/pp-core/references/sketch-tooling.md +4 -0
  114. package/pp-core/references/tdd.md +1 -0
  115. package/pp-core/templates/claude-md.md +1 -1
  116. package/pp-core/templates/phase-prompt.md +6 -2
  117. package/pp-core/templates/prototype-review/review.js +303 -0
  118. package/pp-core/templates/state.md +6 -0
  119. package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -1
  120. package/pp-core/workflows/add-backlog.md +1 -1
  121. package/pp-core/workflows/add-phase.md +1 -1
  122. package/pp-core/workflows/add-tests.md +1 -1
  123. package/pp-core/workflows/add-todo.md +1 -1
  124. package/pp-core/workflows/ai-integration-phase.md +2 -2
  125. package/pp-core/workflows/audit-fix.md +12 -16
  126. package/pp-core/workflows/audit-milestone.md +2 -2
  127. package/pp-core/workflows/audit-uat.md +1 -1
  128. package/pp-core/workflows/autonomous.md +68 -306
  129. package/pp-core/workflows/check-todos.md +1 -1
  130. package/pp-core/workflows/cleanup.md +1 -1
  131. package/pp-core/workflows/code-review-fix.md +2 -2
  132. package/pp-core/workflows/code-review.md +20 -8
  133. package/pp-core/workflows/complete-milestone.md +41 -60
  134. package/pp-core/workflows/debug.md +1 -1
  135. package/pp-core/workflows/diagnose-issues.md +1 -1
  136. package/pp-core/workflows/discovery-sprint/gap-analysis.md +140 -0
  137. package/pp-core/workflows/discovery-sprint/input-package.md +140 -0
  138. package/pp-core/workflows/discovery-sprint/prefill.md +92 -0
  139. package/pp-core/workflows/discovery-sprint/prototype.md +257 -0
  140. package/pp-core/workflows/discovery-sprint/rebaseline.md +276 -0
  141. package/pp-core/workflows/discovery-sprint/session-13-gates.md +24 -0
  142. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +92 -0
  143. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +98 -0
  144. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +215 -0
  145. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +146 -0
  146. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +98 -0
  147. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +107 -0
  148. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +98 -0
  149. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +85 -0
  150. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +109 -0
  151. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +119 -0
  152. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +92 -0
  153. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +98 -0
  154. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +205 -0
  155. package/pp-core/workflows/discovery-sprint/templates/context-template.md +266 -0
  156. package/pp-core/workflows/discovery-sprint.md +79 -1714
  157. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +5 -5
  158. package/pp-core/workflows/discovery-to-build/steps/screen-inventory-gate.md +70 -0
  159. package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +54 -0
  160. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +184 -0
  161. package/pp-core/workflows/discovery-to-build.md +21 -238
  162. package/pp-core/workflows/discuss-phase/modes/advisor.md +1 -1
  163. package/pp-core/workflows/discuss-phase/modes/auto.md +1 -1
  164. package/pp-core/workflows/discuss-phase/modes/chain.md +1 -1
  165. package/pp-core/workflows/discuss-phase-assumptions.md +1 -1
  166. package/pp-core/workflows/discuss-phase.md +11 -4
  167. package/pp-core/workflows/do.md +4 -4
  168. package/pp-core/workflows/docs-update.md +1 -1
  169. package/pp-core/workflows/edit-phase.md +1 -1
  170. package/pp-core/workflows/eval-review.md +1 -1
  171. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +45 -0
  172. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +1 -1
  173. package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  174. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +90 -0
  175. package/pp-core/workflows/execute-phase/steps/install-run.md +29 -0
  176. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +23 -0
  177. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +76 -77
  178. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +72 -0
  179. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +5 -5
  180. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +19 -112
  181. package/pp-core/workflows/execute-phase.md +104 -218
  182. package/pp-core/workflows/execute-plan.md +6 -2
  183. package/pp-core/workflows/explore.md +1 -1
  184. package/pp-core/workflows/extract-learnings.md +1 -1
  185. package/pp-core/workflows/forensics.md +4 -4
  186. package/pp-core/workflows/graduation.md +1 -1
  187. package/pp-core/workflows/health.md +1 -1
  188. package/pp-core/workflows/help/modes/brief.md +1 -0
  189. package/pp-core/workflows/help/modes/default.md +2 -1
  190. package/pp-core/workflows/help/modes/full.md +12 -5
  191. package/pp-core/workflows/import.md +1 -1
  192. package/pp-core/workflows/ingest-docs.md +1 -1
  193. package/pp-core/workflows/init.md +18 -2
  194. package/pp-core/workflows/insert-phase.md +1 -1
  195. package/pp-core/workflows/list-workspaces.md +1 -1
  196. package/pp-core/workflows/manager.md +3 -3
  197. package/pp-core/workflows/map-codebase.md +2 -2
  198. package/pp-core/workflows/milestone-summary.md +4 -4
  199. package/pp-core/workflows/mvp-phase.md +1 -1
  200. package/pp-core/workflows/new-milestone.md +7 -8
  201. package/pp-core/workflows/new-project.md +6 -1
  202. package/pp-core/workflows/new-workspace.md +1 -1
  203. package/pp-core/workflows/next.md +2 -2
  204. package/pp-core/workflows/note.md +2 -2
  205. package/pp-core/workflows/pause-work.md +1 -1
  206. package/pp-core/workflows/plan-milestone-gaps.md +1 -1
  207. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +111 -0
  208. package/pp-core/workflows/plan-phase/steps/phase-tier.md +34 -0
  209. package/pp-core/workflows/plan-phase.md +43 -167
  210. package/pp-core/workflows/plan-review-convergence.md +2 -2
  211. package/pp-core/workflows/plant-seed.md +1 -1
  212. package/pp-core/workflows/pr-branch.md +1 -1
  213. package/pp-core/workflows/profile-user.md +1 -1
  214. package/pp-core/workflows/progress.md +3 -3
  215. package/pp-core/workflows/quick.md +4 -2
  216. package/pp-core/workflows/remove-phase.md +1 -1
  217. package/pp-core/workflows/remove-workspace.md +1 -1
  218. package/pp-core/workflows/resume-project.md +6 -12
  219. package/pp-core/workflows/review.md +1 -1
  220. package/pp-core/workflows/scan.md +1 -1
  221. package/pp-core/workflows/secure-phase.md +2 -2
  222. package/pp-core/workflows/settings-advanced.md +1 -1
  223. package/pp-core/workflows/settings-integrations.md +1 -1
  224. package/pp-core/workflows/settings.md +1 -1
  225. package/pp-core/workflows/ship.md +26 -9
  226. package/pp-core/workflows/sketch-wrap-up.md +1 -1
  227. package/pp-core/workflows/sketch.md +2 -1
  228. package/pp-core/workflows/spec-phase.md +1 -1
  229. package/pp-core/workflows/spike-wrap-up.md +1 -1
  230. package/pp-core/workflows/spike.md +1 -1
  231. package/pp-core/workflows/stats.md +2 -2
  232. package/pp-core/workflows/thread.md +1 -1
  233. package/pp-core/workflows/transition.md +5 -9
  234. package/pp-core/workflows/ui-phase.md +23 -2
  235. package/pp-core/workflows/ui-review.md +1 -1
  236. package/pp-core/workflows/ultraplan-phase.md +2 -2
  237. package/pp-core/workflows/validate-phase.md +2 -2
  238. package/pp-core/workflows/vault-write-back.md +1 -1
  239. package/pp-core/workflows/verify-phase.md +29 -26
  240. package/pp-core/workflows/verify-work.md +14 -10
  241. package/scripts/base64-scan.sh +7 -41
  242. package/scripts/changeset/cli.cjs +25 -5
  243. package/scripts/dev-env.cjs +462 -0
  244. package/scripts/lib/allowlist-ratchet.cjs +103 -1
  245. package/scripts/lib/size-budget.cjs +311 -0
  246. package/scripts/lint-neutrality-names.cjs +694 -0
  247. package/scripts/lint-org-coordinates.allowlist.json +35 -0
  248. package/scripts/lint-org-coordinates.cjs +426 -0
  249. package/scripts/lint-skill-deps.cjs +48 -9
  250. package/scripts/lint-test-file-count.cjs +17 -2
  251. package/scripts/prompt-injection-scan.sh +41 -20
  252. package/scripts/sync-manifest-versions.cjs +41 -7
  253. package/scripts/sync-runtime-launcher.cjs +106 -1
  254. package/scripts/update-size-baseline.cjs +274 -30
  255. package/scripts/workflow-size.cjs +61 -9
  256. package/commands/pp-build-handoff.md +0 -46
  257. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +0 -94
@@ -29,7 +29,7 @@ Then verify each level against the actual codebase.
29
29
  Load phase operation context:
30
30
 
31
31
  ```bash
32
- _PP_SHIM_NAME="pp-tools.cjs"; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; pp_run() { "$PP_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
32
+ _PP_SHIM_NAME="pp-tools.cjs"; _pp_root() { ( for _v in $(env | sed -n 's/^\(GIT_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$_v"; done; _c=$(git rev-parse --git-common-dir 2>/dev/null); if [ "${_c##*/}" = .git ] && cd "$_c/.." 2>/dev/null; then pwd -P; else git rev-parse --show-toplevel 2>/dev/null || pwd; fi ); }; pp_run() { case "$PP_TOOLS" in *.cjs) node "$PP_TOOLS" "$@";; *) "$PP_TOOLS" "$@";; esac; }; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(_pp_root)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then :; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
33
33
  INIT=$(pp_run query init.phase-op "${PHASE_ARG}")
34
34
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
35
35
  ```
@@ -266,40 +266,43 @@ inspecting static artifacts.
266
266
  **Step 1: Run test suite**
267
267
 
268
268
  ```bash
269
- # Resolve test command: project config > Makefile > language sniff
270
- TEST_CMD=$(pp_run query config-get workflow.test_command --default "" 2>/dev/null || true)
269
+ # Resolve test command: project config first, else the shared sniff ladder
270
+ PP_GATE_BIN=$(printf '%s\n' "${PP_TOOLS:+${PP_TOOLS%/*}:}$PATH" | tr : '\n' | while IFS= read -r d; do if [ -r "$d/detect-build-commands.sh" ]; then printf %s "$d"; break; fi; done)
271
+ TEST_CMD=$(pp_run query config-get workflow.test_command --default "" --raw 2>/dev/null || true)
271
272
  if [ -z "$TEST_CMD" ]; then
272
- if [ -f "Makefile" ] && grep -q "^test:" Makefile; then
273
- TEST_CMD="make test"
274
- elif [ -f "Justfile" ] || [ -f "justfile" ]; then
275
- TEST_CMD="just test"
276
- elif [ -f "package.json" ]; then
277
- TEST_CMD="npm test"
278
- elif [ -f "Cargo.toml" ]; then
279
- TEST_CMD="cargo test"
280
- elif [ -f "go.mod" ]; then
281
- TEST_CMD="go test ./..."
282
- elif [ -f "pyproject.toml" ] || [ -f "requirements.txt" ]; then
283
- TEST_CMD="python -m pytest -q --tb=short 2>&1 || uv run python -m pytest -q --tb=short"
273
+ if [ -r "$PP_GATE_BIN/detect-build-commands.sh" ]; then
274
+ . "$PP_GATE_BIN/detect-build-commands.sh"; TEST_CMD=$(pp_detect_test_cmd)
284
275
  else
285
- TEST_CMD="false"
286
- echo "⚠ No test runner detected — skipping test suite"
276
+ echo "✗ pp-core sniff ladder not found beside pp-tools — cannot detect a test command"
287
277
  fi
288
278
  fi
289
- # Detect test runner and run all tests (timeout: 5 minutes)
279
+ # Run all tests through the shared portable timeout wrapper (5-minute budget)
290
280
  TEST_EXIT=0
291
- timeout 300 bash -c "$TEST_CMD" 2>&1
292
- TEST_EXIT=$?
293
- if [ "${TEST_EXIT}" -eq 0 ]; then
294
- echo "✓ Test suite passed"
295
- elif [ "${TEST_EXIT}" -eq 124 ]; then
296
- echo "⚠ Test suite timed out after 5 minutes"
281
+ if [ -z "$TEST_CMD" ]; then
282
+ echo "⚠ Test suite NOT RUN — no test runner detected (not a pass, not a failure)"
283
+ elif [ ! -r "$PP_GATE_BIN/portable-timeout.sh" ]; then
284
+ echo "✗ Test suite NEVER RAN — portable-timeout.sh not found beside pp-tools; refusing to run unbounded (D-09-03)"
285
+ TEST_EXIT=127
297
286
  else
298
- echo "✗ Test suite failed (exit code ${TEST_EXIT})"
287
+ . "$PP_GATE_BIN/portable-timeout.sh"
288
+ run_with_timeout 300 bash -c "$TEST_CMD" 2>&1
289
+ TEST_EXIT=$?
290
+ if [ "${TEST_EXIT}" -eq 0 ]; then
291
+ echo "✓ Test suite passed"
292
+ elif is_timeout_exit "${TEST_EXIT}"; then
293
+ echo "⚠ Test suite timed out after 300 seconds (exit ${TEST_EXIT}) — not counted as a failure"
294
+ elif [ "${TEST_EXIT}" -eq 127 ]; then
295
+ echo "✗ Test suite NEVER RAN — command not found (exit 127): ${TEST_CMD}"
296
+ else
297
+ echo "✗ Test suite failed (exit code ${TEST_EXIT})"
298
+ fi
299
299
  fi
300
300
  ```
301
301
 
302
- Record: total tests, passed, failed, coverage (if available).
302
+ Record: total tests, passed, failed, coverage (if available). If the `NOT RUN` line printed,
303
+ record the suite as SKIPPED — not as passed and not as a behavioral failure. If the
304
+ `NEVER RAN` line printed, record it as BLOCKER: nothing was executed, so nothing is verified.
305
+ A `timed out` line (exit 124 or 142) is advisory, not a failure.
303
306
 
304
307
  **If any tests fail:** Mark as `behavioral_failures` — these are BLOCKER severity
305
308
  regardless of whether static checks passed. A phase cannot be verified if tests fail.
@@ -37,18 +37,20 @@ No Pass/Fail buttons. No severity questions. Just: "Here's what should happen. D
37
37
  If $ARGUMENTS contains a phase number, load context:
38
38
 
39
39
  ```bash
40
- _PP_SHIM_NAME="pp-tools.cjs"; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; pp_run() { "$PP_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
40
+ _PP_SHIM_NAME="pp-tools.cjs"; _pp_root() { ( for _v in $(env | sed -n 's/^\(GIT_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$_v"; done; _c=$(git rev-parse --git-common-dir 2>/dev/null); if [ "${_c##*/}" = .git ] && cd "$_c/.." 2>/dev/null; then pwd -P; else git rev-parse --show-toplevel 2>/dev/null || pwd; fi ); }; pp_run() { case "$PP_TOOLS" in *.cjs) node "$PP_TOOLS" "$@";; *) "$PP_TOOLS" "$@";; esac; }; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(_pp_root)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then :; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
41
41
  GSD_WS=""
42
42
  echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+')
43
43
  PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs)
44
+ TIER_FLAG=$(printf '%s\n' "$ARGUMENTS" | grep -oE -- '--tier[[:space:]=]+(light|standard|heavy)' | head -1 | sed -E 's/^--tier[[:space:]=]+/--tier=/')
45
+ PHASE_ARG=$(echo "$PHASE_ARG" | sed -E 's/--tier[[:space:]]+[a-z]+//g' | xargs)
44
46
 
45
- INIT=$(pp_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})
47
+ INIT=$(pp_run query init.verify-work "${PHASE_ARG}" ${GSD_WS} ${TIER_FLAG})
46
48
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
47
49
  AGENT_SKILLS_PLANNER=$(pp_run query agent-skills pp-planner)
48
50
  AGENT_SKILLS_CHECKER=$(pp_run query agent-skills pp-plan-checker)
49
51
  ```
50
52
 
51
- Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
53
+ Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `plan_checker_enabled`, `plan_check_passes`, `phase_tier` (the phase's ceremony tier), `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
52
54
 
53
55
  ```bash
54
56
  # MVP mode detection via the centralized phase.mvp-mode resolver.
@@ -111,7 +113,7 @@ Continue to `create_uat_file`.
111
113
  Before UAT, check UI capability activation and whether Playwright/Puppeteer MCP tools are available.
112
114
 
113
115
  ```bash
114
- PLAN_HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw)
116
+ PLAN_HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw --phase "${phase_number}" ${TIER_FLAG}) || exit 1
115
117
  UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
116
118
  ```
117
119
 
@@ -458,7 +460,7 @@ Present summary:
458
460
  **If issues == 0:**
459
461
 
460
462
  ```bash
461
- VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw)
463
+ VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw --phase "${phase_number}" ${GSD_WS} ${TIER_FLAG}) || exit 1
462
464
  SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
463
465
  ```
464
466
 
@@ -619,6 +621,8 @@ On return:
619
621
  <step name="verify_gap_plans">
620
622
  **Verify fix plans with checker:**
621
623
 
624
+ **Skip if** `plan_checker_enabled` is false (the tier, or the project's explicit `false`) — go to `present_ready`.
625
+
622
626
  Run this Bash command to emit the branded Validate stage banner (renders on stderr, auto-degrades on non-TTY — do NOT print an inline ASCII banner, that would double-print):
623
627
  ```bash
624
628
  pp_run banner validate "VERIFYING"
@@ -643,7 +647,7 @@ Agent(
643
647
 
644
648
  <files_to_read>
645
649
  - {phase_dir}/*-PLAN.md (Plans to verify)
646
- - CLAUDE.md (Project instructions, if exists — follow project-specific guidelines and coding conventions)
650
+ - ./CLAUDE.md or ./.claude/CLAUDE.md (Project instructions, if either exists — follow project-specific guidelines and coding conventions)
647
651
  </files_to_read>
648
652
 
649
653
  ${AGENT_SKILLS_CHECKER}
@@ -670,11 +674,11 @@ On return:
670
674
  </step>
671
675
 
672
676
  <step name="revision_loop">
673
- **Iterate planner ↔ checker until plans pass (max 3):**
677
+ **Iterate planner ↔ checker until plans pass (max `plan_check_passes`):**
674
678
 
675
- **If iteration_count < 3:**
679
+ **If iteration_count < plan_check_passes:**
676
680
 
677
- Display: `Sending back to planner for revision... (iteration {N}/3)`
681
+ Display: `Sending back to planner for revision... (iteration {N}/{plan_check_passes})`
678
682
 
679
683
  Spawn pp-planner with revision context:
680
684
 
@@ -713,7 +717,7 @@ Do NOT replan from scratch unless issues are fundamental.
713
717
  After planner returns → spawn checker again (verify_gap_plans logic)
714
718
  Increment iteration_count
715
719
 
716
- **If iteration_count >= 3:**
720
+ **If iteration_count >= plan_check_passes:**
717
721
 
718
722
  Display: `Max iterations reached. {N} issues remain.`
719
723
 
@@ -40,47 +40,13 @@ MIN_BLOB_LENGTH=40
40
40
  MAX_LINE_BYTES=1048576
41
41
 
42
42
  # -- Portable timeout wrapper (#116) ------------------------------------------
43
- # macOS does not ship GNU coreutils timeout. We probe for it (or gtimeout
44
- # from homebrew coreutils), falling back to perl alarm(N)+exec.
45
- # Exit codes: GNU timeout uses 124 on timeout; perl SIGALRM produces 142.
46
- # Both are treated as timeout exits by is_timeout_exit() below.
47
- # Usage: run_with_timeout <seconds> <command> [args...]
48
- _TIMEOUT_CMD=""
49
- # shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
50
- _init_timeout_cmd() {
51
- if [[ -n "$_TIMEOUT_CMD" ]]; then return; fi
52
- if command -v timeout >/dev/null 2>&1; then
53
- _TIMEOUT_CMD="timeout"
54
- elif command -v gtimeout >/dev/null 2>&1; then
55
- _TIMEOUT_CMD="gtimeout"
56
- else
57
- _TIMEOUT_CMD="perl_alarm"
58
- fi
59
- }
60
-
61
- # shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
62
- run_with_timeout() {
63
- local secs="$1"; shift
64
- _init_timeout_cmd
65
- case "$_TIMEOUT_CMD" in
66
- timeout|gtimeout)
67
- "$_TIMEOUT_CMD" "$secs" "$@"
68
- ;;
69
- perl_alarm)
70
- # perl sets SIGALRM after N seconds, then exec()s the command.
71
- # Exit 142 (SIGALRM) when timed out.
72
- perl -e '
73
- my $secs = shift @ARGV;
74
- alarm($secs);
75
- exec(@ARGV) or die "exec: $!\n";
76
- ' -- "$secs" "$@"
77
- ;;
78
- esac
79
- }
80
-
81
- # is_timeout_exit: returns 0 (true) if rc indicates a timeout kill.
82
- # shellcheck disable=SC2329 # intentionally defined for use by callers; not called in main loop
83
- is_timeout_exit() { [[ "$1" -eq 124 || "$1" -eq 142 ]]; }
43
+ # D-09-03: ONE portable timeout implementation exists in this repository and it
44
+ # lives in pp-core/bin/ so an INSTALLED workflow can source it too. This script
45
+ # is not installed (no scripts/*.sh entry in pp-file-manifest.json), so the
46
+ # wrapper could not stay here. Resolved relative to this script, so `bash
47
+ # scripts/base64-scan.sh` works from any cwd.
48
+ # shellcheck source=../pp-core/bin/portable-timeout.sh
49
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/pp-core/bin/portable-timeout.sh"
84
50
 
85
51
 
86
52
  # ─── Injection Patterns (decoded content) ────────────────────────────────────
@@ -22,10 +22,6 @@ const { parseFragment } = require('./parse.cjs');
22
22
  const { renderChangelog } = require('./render.cjs');
23
23
  const { serializeChangelog, parseChangelog } = require('./serialize.cjs');
24
24
  const { renderGithubReleaseNotes } = require('./github-release-notes.cjs');
25
- const {
26
- compareSemverCore,
27
- isStableTripletSemver,
28
- } = require('../../pp-core/bin/lib/semver-compare.cjs');
29
25
  const { packageName, repoSlug: defaultRepoSlug } = require('../../pp-core/bin/lib/package-identity.cjs');
30
26
 
31
27
  function parseArgs(argv) {
@@ -94,6 +90,29 @@ function parseArgs(argv) {
94
90
  return { ok: true, opts };
95
91
  }
96
92
 
93
+ // Bare-checkout verify. A release tag job may run `verify` on a bare checkout:
94
+ // no `npm ci`, no `build:lib`, and the compiled semver module under
95
+ // pp-core/bin/lib is gitignored build output. Requiring that module at the top
96
+ // of this file made `verify` (and `render`) die at require time on such a
97
+ // checkout.
98
+ // `verify` and `render` therefore require node builtins and tracked files only;
99
+ // the compiled dependency is loaded lazily by `extract`, which the update
100
+ // workflow runs on built trees.
101
+ //
102
+ // isStableTripletSemver is a byte-for-behaviour twin of the function of the same
103
+ // name in src/semver-compare.cts. The parity arm in tests/changeset-cli.test.cjs
104
+ // (a fixed corpus plus a fast-check property) keeps the two equal: change both
105
+ // or neither.
106
+ const STABLE_TRIPLET_RE = /^\d+\.\d+\.\d+$/;
107
+
108
+ function isStableTripletSemver(v) {
109
+ return STABLE_TRIPLET_RE.test(String(v || '').replace(/^v/, ''));
110
+ }
111
+
112
+ function requireSemverCompare() {
113
+ return require('../../pp-core/bin/lib/semver-compare.cjs');
114
+ }
115
+
97
116
  function listFragmentFiles(changesetDir) {
98
117
  if (!fs.existsSync(changesetDir)) return [];
99
118
  return fs.readdirSync(changesetDir)
@@ -362,6 +381,7 @@ function cmdExtract(opts) {
362
381
  const text = fs.readFileSync(changelogPath, 'utf8');
363
382
  const { releases } = parseChangelog(text);
364
383
 
384
+ const { compareSemverCore } = requireSemverCompare();
365
385
  const matched = releases.filter((rel) => {
366
386
  if (rel.version === 'Unreleased') return false;
367
387
  // Extract mode intentionally operates on stable releases only.
@@ -594,4 +614,4 @@ function main() {
594
614
 
595
615
  if (require.main === module) runMain(main);
596
616
 
597
- module.exports = { cmdRender, cmdExtract, cmdVerify, cmdGithubReleaseNotes, parseArgs, splitChangelog, assembleChangelog, listFragmentFiles, usage };
617
+ module.exports = { cmdRender, cmdExtract, cmdVerify, cmdGithubReleaseNotes, parseArgs, splitChangelog, assembleChangelog, listFragmentFiles, usage, isStableTripletSemver };