@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,483 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const yaml = require('js-yaml');
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Policy: native shell per OS
9
+ // ---------------------------------------------------------------------------
10
+ const POLICY = Object.freeze({
11
+ 'ubuntu-latest': 'bash',
12
+ 'ubuntu-22.04': 'bash',
13
+ 'ubuntu-24.04': 'bash',
14
+ 'macos-latest': 'zsh',
15
+ 'macos-13': 'zsh',
16
+ 'macos-14': 'zsh',
17
+ 'macos-15': 'zsh',
18
+ 'windows-latest': 'pwsh',
19
+ 'windows-2022': 'pwsh',
20
+ 'windows-2025': 'pwsh',
21
+ });
22
+
23
+ const VIOLATION = Object.freeze({
24
+ WRONG_SHELL_FOR_OS: 'wrong_shell_for_os',
25
+ MACOS_MISSING_EXPLICIT_ZSH: 'macos_missing_explicit_zsh',
26
+ UNKNOWN_RUNNER: 'unknown_runner',
27
+ UNRESOLVABLE_MATRIX: 'unresolvable_matrix',
28
+ });
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Runner default (GitHub Actions documented defaults, not policy)
32
+ // ---------------------------------------------------------------------------
33
+ function runnerDefault(runner) {
34
+ if (!runner) return null;
35
+ if (runner.startsWith('windows-')) return 'pwsh';
36
+ return 'bash'; // ubuntu-* and macos-* both default to bash on GHA
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Matrix expansion helpers
41
+ // ---------------------------------------------------------------------------
42
+
43
+ // Build the GitHub Actions Cartesian product of the given base-list matrix
44
+ // keys. Returns one context object per realized job, each mapping every key
45
+ // to a stringified value. A single key yields one context per value (identical
46
+ // to the legacy base-list expansion); N keys yield the full cross-product.
47
+ // Values are stringified to match runner-label comparison and matrix-expression
48
+ // resolution, which operate on strings.
49
+ function cartesianProduct(keys, matrix) {
50
+ return keys.reduce(
51
+ (contexts, key) =>
52
+ contexts.flatMap((context) =>
53
+ matrix[key].map((value) => ({ ...context, [key]: String(value) })),
54
+ ),
55
+ [{}],
56
+ );
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Matrix expansion
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /**
64
+ * Expand a runs-on expression against a job's strategy.matrix.
65
+ * Returns an array of { runner: string, resolvable: boolean, context: object }
66
+ * objects where `context` holds ALL key→value pairs for the realization row
67
+ * (so shell expressions like ${{ matrix.shell }} can be resolved against it).
68
+ * 'resolvable: false' means the expression was an unresolved matrix ref.
69
+ */
70
+ function expandRunsOn(runsOnRaw, matrix) {
71
+ if (!runsOnRaw) return [];
72
+
73
+ const raw = String(runsOnRaw).trim();
74
+
75
+ // Detect matrix expression: ${{ matrix.X }} or ${{ matrix['X'] }}
76
+ const matrixExprRe = /\$\{\{\s*matrix\.(\w+)\s*\}\}/;
77
+ const match = raw.match(matrixExprRe);
78
+
79
+ if (!match) {
80
+ // Literal runner label
81
+ return [{ runner: raw, resolvable: true, context: {} }];
82
+ }
83
+
84
+ const key = match[1];
85
+
86
+ if (!matrix) {
87
+ return [{ runner: raw, resolvable: false, context: {} }];
88
+ }
89
+
90
+ const realizations = [];
91
+
92
+ // matrix.include entries carry complete row context — prefer them as they
93
+ // contain all keys (os, node-version, shell, full_only, etc.).
94
+ // Each include row is a distinct CI realization and must be validated
95
+ // independently — even if two rows share the same runner label, their
96
+ // contexts (and therefore effective shells) may differ.
97
+ if (Array.isArray(matrix.include)) {
98
+ for (const entry of matrix.include) {
99
+ if (entry && entry[key] != null) {
100
+ const runner = String(entry[key]);
101
+ // Clone all keys from include row as the realization context
102
+ const context = {};
103
+ for (const [k, v] of Object.entries(entry)) {
104
+ context[k] = v != null ? String(v) : '';
105
+ }
106
+ realizations.push({ runner, resolvable: true, context });
107
+ }
108
+ }
109
+ }
110
+
111
+ // GitHub Actions realizes one job per element of the Cartesian product of all
112
+ // base-list matrix keys (every matrix.<k> that is an array, excluding the
113
+ // include/exclude control keys), and each realized job's context carries a
114
+ // value for EVERY matrix key — so ${{ matrix.<key> }} references (e.g. in a
115
+ // shell: field) resolve against any realization, not only the runs-on key.
116
+ // Single-axis matrices yield exactly one realization per value, identical to
117
+ // the prior behavior; only multi-axis matrices change shape.
118
+ // Each realization is pushed unconditionally — deduplicating by runner label
119
+ // would collapse distinct Cartesian rows (e.g. matrix.os: [macos-latest,
120
+ // macos-latest] paired with different shells) and hide policy violations.
121
+ if (Array.isArray(matrix[key])) {
122
+ const baseListKeys = Object.keys(matrix).filter(
123
+ (k) => k !== 'include' && k !== 'exclude' && Array.isArray(matrix[k]),
124
+ );
125
+ for (const context of cartesianProduct(baseListKeys, matrix)) {
126
+ realizations.push({ runner: context[key], resolvable: true, context });
127
+ }
128
+ }
129
+
130
+ // matrix.exclude: remove matches by runner label (first match only).
131
+ // KNOWN LIMITATION (out of scope for #435, tracked as a follow-up): this
132
+ // matches on the runs-on key's runner label rather than the full exclude
133
+ // tuple, so a multi-axis exclude like { os: macos-latest, shell: bash } can
134
+ // remove the wrong cross-product cell. Full GitHub Actions tuple-match
135
+ // (including the include-rows-are-not-excluded rule) is deferred; #435 scopes
136
+ // only the base-list cross-product expansion above.
137
+ if (Array.isArray(matrix.exclude)) {
138
+ for (const excl of matrix.exclude) {
139
+ if (excl && excl[key] != null) {
140
+ const exclRunner = String(excl[key]);
141
+ const idx = realizations.findIndex(r => r.runner === exclRunner);
142
+ if (idx !== -1) realizations.splice(idx, 1);
143
+ }
144
+ }
145
+ }
146
+
147
+ if (realizations.length === 0) {
148
+ // Could not resolve — no concrete values found
149
+ return [{ runner: raw, resolvable: false, context: {} }];
150
+ }
151
+
152
+ return realizations;
153
+ }
154
+
155
+ // ---------------------------------------------------------------------------
156
+ // Matrix expression resolution
157
+ // ---------------------------------------------------------------------------
158
+
159
+ /**
160
+ * If `expr` is a `${{ matrix.<key> }}` expression, look up the value in
161
+ * `realizationContext` (a plain-object snapshot of one matrix.include row).
162
+ * Returns:
163
+ * { resolved: true, value: string } — expression resolved to a concrete value
164
+ * { resolved: false, key: string } — matrix key absent in this realization
165
+ * null — `expr` is not a matrix expression
166
+ */
167
+ function resolveMatrixExpr(expr, realizationContext) {
168
+ if (!expr || typeof expr !== 'string') return null;
169
+ const m = expr.match(/^\s*\$\{\{\s*matrix\.(\w+)\s*\}\}\s*$/);
170
+ if (!m) return null;
171
+ const key = m[1];
172
+ if (!realizationContext || !(key in realizationContext)) {
173
+ return { resolved: false, key };
174
+ }
175
+ return { resolved: true, value: String(realizationContext[key]) };
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Effective-shell resolution
180
+ // ---------------------------------------------------------------------------
181
+
182
+ /**
183
+ * Given a step's shell, job defaults, workflow defaults, runner, and the
184
+ * current matrix realization context, return the effective shell that will
185
+ * actually execute.
186
+ *
187
+ * Matrix expressions (`${{ matrix.shell }}`) in any shell field are resolved
188
+ * against `realizationContext` (a plain object of key→value for the current
189
+ * matrix.include row).
190
+ *
191
+ * Returns:
192
+ * { shell: string, unresolvable: false } — concrete shell value
193
+ * { shell: null, unresolvable: true, key: string } — matrix expr present but key missing
194
+ */
195
+ function effectiveShell(stepShell, jobDefaultsShell, workflowDefaultsShell, runner, realizationContext) {
196
+ for (const raw of [stepShell, jobDefaultsShell, workflowDefaultsShell]) {
197
+ if (!raw) continue;
198
+ const mx = resolveMatrixExpr(raw, realizationContext);
199
+ if (mx !== null) {
200
+ // It's a matrix expression
201
+ if (!mx.resolved) {
202
+ return { shell: null, unresolvable: true, key: mx.key };
203
+ }
204
+ return { shell: mx.value, unresolvable: false };
205
+ }
206
+ // Literal value
207
+ return { shell: raw, unresolvable: false };
208
+ }
209
+ // Nothing set at any level — use runner default
210
+ return { shell: runnerDefault(runner), unresolvable: false };
211
+ }
212
+
213
+ // ---------------------------------------------------------------------------
214
+ // Violation detection
215
+ // ---------------------------------------------------------------------------
216
+ /**
217
+ * Determines whether a step/runner combination violates shell policy.
218
+ *
219
+ * `rawStepShell`, `rawJobDefaultsShell`, `rawWorkflowDefaultsShell` are the
220
+ * raw (possibly matrix-expression) values before resolution. They're used
221
+ * only for the MACOS_MISSING_EXPLICIT_ZSH sub-classification: that violation
222
+ * fires only when nothing is set at any level (all three are null/empty AND
223
+ * the runner default is wrong).
224
+ */
225
+ function detectViolation(runner, resolvedShell, rawStepShell, rawJobDefaultsShell, rawWorkflowDefaultsShell) {
226
+ if (!(runner in POLICY)) {
227
+ return VIOLATION.UNKNOWN_RUNNER;
228
+ }
229
+ const expected = POLICY[runner];
230
+ // GHA accepts custom shells as a format string containing '{0}' (e.g. 'zsh {0}').
231
+ // Per https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
232
+ // the shell name before the space is the executable; strip the format suffix before
233
+ // comparing against the policy so 'zsh {0}' satisfies the 'zsh' requirement.
234
+ const normalizedShell = resolvedShell ? resolvedShell.replace(/\s+\{0\}$/, '') : resolvedShell;
235
+ if (normalizedShell !== expected) {
236
+ // Specific subtype for macOS missing explicit zsh:
237
+ // fires only when no shell is set at any level (inherited runner default).
238
+ if (runner.startsWith('macos-') && !rawStepShell && !rawJobDefaultsShell && !rawWorkflowDefaultsShell) {
239
+ return VIOLATION.MACOS_MISSING_EXPLICIT_ZSH;
240
+ }
241
+ return VIOLATION.WRONG_SHELL_FOR_OS;
242
+ }
243
+ return null;
244
+ }
245
+
246
+ // ---------------------------------------------------------------------------
247
+ // Source map: find line numbers
248
+ // ---------------------------------------------------------------------------
249
+
250
+ /**
251
+ * Find the line number of a string in YAML text.
252
+ * Returns 1-based line number of the first occurrence at or after startLine.
253
+ */
254
+ function findLineNumber(yamlText, searchStr, startLine) {
255
+ const lines = yamlText.split('\n');
256
+ const start = Math.max(0, (startLine || 1) - 1);
257
+ for (let i = start; i < lines.length; i++) {
258
+ if (lines[i].includes(searchStr)) {
259
+ return i + 1;
260
+ }
261
+ }
262
+ // Fall back to scanning from beginning
263
+ for (let i = 0; i < lines.length; i++) {
264
+ if (lines[i].includes(searchStr)) {
265
+ return i + 1;
266
+ }
267
+ }
268
+ return 1;
269
+ }
270
+
271
+ // ---------------------------------------------------------------------------
272
+ // Core inspector
273
+ // ---------------------------------------------------------------------------
274
+
275
+ /**
276
+ * inspectWorkflow(yamlText, { filePath }) → structured inspection result
277
+ */
278
+ function inspectWorkflow(yamlText, { filePath = '<unknown>' } = {}) {
279
+ let doc;
280
+ try {
281
+ doc = yaml.load(yamlText, { schema: yaml.DEFAULT_SCHEMA });
282
+ } catch (e) {
283
+ return {
284
+ filePath,
285
+ jobs: [],
286
+ workflowDefaultsShell: null,
287
+ parseError: e.message,
288
+ };
289
+ }
290
+
291
+ if (!doc || typeof doc !== 'object') {
292
+ return { filePath, jobs: [], workflowDefaultsShell: null };
293
+ }
294
+
295
+ const workflowDefaultsShell =
296
+ doc.defaults?.run?.shell ?? null;
297
+
298
+ const jobs = [];
299
+
300
+ for (const [jobId, jobDef] of Object.entries(doc.jobs || {})) {
301
+ if (!jobDef || typeof jobDef !== 'object') continue;
302
+
303
+ const runsOnRaw = jobDef['runs-on'];
304
+ const matrix = jobDef.strategy?.matrix ?? null;
305
+ const jobDefaultsShell = jobDef.defaults?.run?.shell ?? null;
306
+
307
+ const runsOnStr = runsOnRaw != null ? String(runsOnRaw) : '';
308
+ const runsOnExpressions = [runsOnStr];
309
+ const runnerRealizations = expandRunsOn(runsOnStr, matrix);
310
+
311
+ const steps = [];
312
+
313
+ for (const [stepIndex, step] of (jobDef.steps || []).entries()) {
314
+ if (!step || typeof step !== 'object') continue;
315
+
316
+ // Only check steps that actually run shell scripts (have `run:`)
317
+ if (!step.run) continue;
318
+
319
+ const stepShell = step.shell ?? null;
320
+ const stepName = step.name ?? `step-${stepIndex}`;
321
+
322
+ for (const { runner, resolvable, context: realizationContext } of runnerRealizations) {
323
+ if (!resolvable) {
324
+ // Can't resolve runner — emit UNRESOLVABLE_MATRIX
325
+ const lineNum = findLineNumber(yamlText, stepName !== `step-${stepIndex}` ? stepName : String(step.run).slice(0, 20));
326
+ steps.push({
327
+ index: stepIndex,
328
+ name: stepName,
329
+ stepShell,
330
+ effectiveShell: null,
331
+ runner,
332
+ violation: VIOLATION.UNRESOLVABLE_MATRIX,
333
+ evidence: {
334
+ line: lineNum,
335
+ snippet: `runs-on: ${runsOnStr} (unresolvable matrix expression)`,
336
+ },
337
+ });
338
+ continue;
339
+ }
340
+
341
+ const effResult = effectiveShell(stepShell, jobDefaultsShell, workflowDefaultsShell, runner, realizationContext);
342
+
343
+ // If a matrix expression referenced a key not present in this realization row
344
+ if (effResult.unresolvable) {
345
+ const lineNum = findLineNumber(yamlText, stepName !== `step-${stepIndex}` ? stepName : String(step.run).slice(0, 20));
346
+ steps.push({
347
+ index: stepIndex,
348
+ name: stepName,
349
+ stepShell,
350
+ effectiveShell: null,
351
+ runner,
352
+ violation: VIOLATION.UNRESOLVABLE_MATRIX,
353
+ evidence: {
354
+ line: lineNum,
355
+ snippet: `matrix.${effResult.key} not present in realization for runner=${runner}`,
356
+ },
357
+ });
358
+ continue;
359
+ }
360
+
361
+ const eff = effResult.shell;
362
+ const violation = detectViolation(runner, eff, stepShell, jobDefaultsShell, workflowDefaultsShell);
363
+
364
+ // Find evidence line: prefer step name, then shell:, then run: content
365
+ let evidenceLine = 1;
366
+ let evidenceSnippet = '';
367
+
368
+ if (stepName !== `step-${stepIndex}`) {
369
+ evidenceLine = findLineNumber(yamlText, stepName);
370
+ evidenceSnippet = `name: ${stepName}`;
371
+ } else if (stepShell) {
372
+ evidenceLine = findLineNumber(yamlText, `shell: ${stepShell}`);
373
+ evidenceSnippet = `shell: ${stepShell}`;
374
+ } else {
375
+ const runSnippet = String(step.run).split('\n')[0].slice(0, 40);
376
+ evidenceLine = findLineNumber(yamlText, runSnippet);
377
+ evidenceSnippet = runSnippet;
378
+ }
379
+
380
+ steps.push({
381
+ index: stepIndex,
382
+ name: stepName,
383
+ stepShell,
384
+ effectiveShell: eff,
385
+ runner,
386
+ violation: violation ?? null,
387
+ evidence: {
388
+ line: evidenceLine,
389
+ snippet: evidenceSnippet,
390
+ },
391
+ });
392
+ }
393
+ }
394
+
395
+ const resolvedRunners = runnerRealizations
396
+ .filter(r => r.resolvable)
397
+ .map(r => r.runner);
398
+
399
+ jobs.push({
400
+ jobId,
401
+ runsOnExpressions,
402
+ resolvedRunners,
403
+ defaultsShell: jobDefaultsShell,
404
+ steps,
405
+ });
406
+ }
407
+
408
+ return {
409
+ filePath,
410
+ jobs,
411
+ workflowDefaultsShell,
412
+ };
413
+ }
414
+
415
+ /**
416
+ * inspectWorkflowFile(absPath) — reads file from disk and calls inspectWorkflow.
417
+ */
418
+ function inspectWorkflowFile(absPath) {
419
+ const text = fs.readFileSync(absPath, 'utf8');
420
+ return inspectWorkflow(text, { filePath: absPath });
421
+ }
422
+
423
+ // ---------------------------------------------------------------------------
424
+ // runPolicyLint
425
+ // ---------------------------------------------------------------------------
426
+
427
+ /**
428
+ * runPolicyLint({ workflowsDir }) → { violations, summary }
429
+ */
430
+ function runPolicyLint({ workflowsDir }) {
431
+ const absDir = path.resolve(workflowsDir);
432
+ const files = fs.readdirSync(absDir)
433
+ .filter(f => f.endsWith('.yml') || f.endsWith('.yaml'))
434
+ .map(f => path.join(absDir, f))
435
+ .sort();
436
+
437
+ const violations = [];
438
+
439
+ for (const filePath of files) {
440
+ const result = inspectWorkflowFile(filePath);
441
+ for (const job of result.jobs) {
442
+ for (const step of job.steps) {
443
+ if (step.violation) {
444
+ violations.push({
445
+ filePath: result.filePath,
446
+ jobId: job.jobId,
447
+ stepIndex: step.index,
448
+ stepName: step.name,
449
+ runner: step.runner,
450
+ effectiveShell: step.effectiveShell,
451
+ stepShell: step.stepShell,
452
+ violation: step.violation,
453
+ evidence: step.evidence,
454
+ });
455
+ }
456
+ }
457
+ }
458
+ }
459
+
460
+ const perViolationType = {};
461
+ for (const v of violations) {
462
+ perViolationType[v.violation] = (perViolationType[v.violation] || 0) + 1;
463
+ }
464
+
465
+ return {
466
+ violations,
467
+ summary: {
468
+ total: violations.length,
469
+ perViolationType,
470
+ },
471
+ };
472
+ }
473
+
474
+ // ---------------------------------------------------------------------------
475
+ // Exports
476
+ // ---------------------------------------------------------------------------
477
+ module.exports = {
478
+ POLICY,
479
+ VIOLATION,
480
+ inspectWorkflow,
481
+ inspectWorkflowFile,
482
+ runPolicyLint,
483
+ };
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @file workflow-size.cjs
5
+ *
6
+ * Single source of truth for measuring workflow `.md` file sizes in bytes.
7
+ *
8
+ * Shared by `tests/workflow-size-budget.test.cjs` (the CI guard) and
9
+ * `scripts/update-size-baseline.cjs` (the baseline generator) so the two can
10
+ * never disagree on HOW a file is measured. A divergence between the generator
11
+ * and the guard would silently mis-record the baseline (issue #1074).
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const WORKFLOWS_DIR = path.join(__dirname, '..', 'pp-core', 'workflows');
18
+
19
+ /**
20
+ * Byte size of a file, counted as on an LF (Unix) checkout.
21
+ *
22
+ * The size budget is calibrated against `wc -c` on a Unix (LF) checkout, but
23
+ * these `.md` files have no `eol=lf` in `.gitattributes`, so Windows checks
24
+ * them out as CRLF. Counting raw on-disk bytes there adds one byte per line,
25
+ * a Windows-only false positive that diverges from the LF calibration basis
26
+ * (issue #683). Stripping CR yields the same LF byte count on every platform.
27
+ * This is still a raw byte count (not a trailing-newline-stripping line count).
28
+ *
29
+ * @param {string} filePath - Absolute or relative path to the file.
30
+ * @returns {number} LF-normalized byte length.
31
+ */
32
+ function lfByteCount(filePath) {
33
+ const content = fs.readFileSync(filePath, 'utf-8');
34
+ return Buffer.byteLength(content.replace(/\r\n/g, '\n'), 'utf-8');
35
+ }
36
+
37
+ /**
38
+ * List top-level workflow stems (filenames without the `.md` extension), sorted.
39
+ * Non-recursive by design: per-mode bodies under `workflows/<name>/modes/` and
40
+ * templates are NOT measured — only the always-loaded top-level workflows.
41
+ *
42
+ * @param {string} [dir] - Workflows directory (defaults to the canonical one).
43
+ * @returns {string[]} Sorted stems, e.g. `['autonomous', 'plan-phase', ...]`.
44
+ */
45
+ function listWorkflowStems(dir = WORKFLOWS_DIR) {
46
+ return fs
47
+ .readdirSync(dir)
48
+ .filter((f) => f.endsWith('.md'))
49
+ .map((f) => f.replace(/\.md$/, ''))
50
+ .sort();
51
+ }
52
+
53
+ /**
54
+ * Measure every top-level `.md` file in `dir`, keyed by filename, byte sizes.
55
+ * Generic over directory and an optional filename predicate — used for both
56
+ * workflows (`pp-core/workflows/*.md`) and agents (`agents/gsd-*.md`) so the
57
+ * size guards and the baseline generator share one measurement path (#1074).
58
+ * Non-recursive by design.
59
+ *
60
+ * @param {string} dir - Directory to scan.
61
+ * @param {function(string): boolean} [predicate] - Filename filter (default: all `.md`).
62
+ * @returns {Object<string, number>} Map of filename → LF byte size, keys sorted.
63
+ */
64
+ function measureMdFiles(dir, predicate = () => true) {
65
+ const out = {};
66
+ const names = fs
67
+ .readdirSync(dir)
68
+ .filter((f) => f.endsWith('.md') && predicate(f))
69
+ .sort();
70
+ for (const name of names) out[name] = lfByteCount(path.join(dir, name));
71
+ return out;
72
+ }
73
+
74
+ /**
75
+ * Measure every top-level workflow file, keyed by filename (`<stem>.md`).
76
+ *
77
+ * @param {string} [dir] - Workflows directory (defaults to the canonical one).
78
+ * @returns {Object<string, number>} Map of `<stem>.md` → LF byte size, sorted.
79
+ */
80
+ function measureWorkflows(dir = WORKFLOWS_DIR) {
81
+ return measureMdFiles(dir);
82
+ }
83
+
84
+ module.exports = {
85
+ WORKFLOWS_DIR,
86
+ lfByteCount,
87
+ listWorkflowStems,
88
+ measureMdFiles,
89
+ measureWorkflows,
90
+ };