@opengsd/gsd-core 1.17.0 → 1.19.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 (369) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.opencode/plugins/gsd-core.js +2 -3
  4. package/agents/gsd-advisor-researcher.compact.md +1 -1
  5. package/agents/gsd-advisor-researcher.md +1 -1
  6. package/agents/gsd-assumptions-analyzer.compact.md +1 -1
  7. package/agents/gsd-assumptions-analyzer.md +1 -1
  8. package/agents/gsd-code-fixer.compact.md +3 -3
  9. package/agents/gsd-code-fixer.md +5 -4
  10. package/agents/gsd-code-reviewer.compact.md +1 -1
  11. package/agents/gsd-code-reviewer.md +1 -1
  12. package/agents/gsd-codebase-mapper.compact.md +1 -1
  13. package/agents/gsd-codebase-mapper.md +1 -1
  14. package/agents/gsd-debugger.md +1 -1
  15. package/agents/gsd-doc-verifier.compact.md +4 -4
  16. package/agents/gsd-doc-verifier.md +4 -3
  17. package/agents/gsd-doc-writer.compact.md +23 -5
  18. package/agents/gsd-doc-writer.md +19 -6
  19. package/agents/gsd-eval-auditor.compact.md +1 -1
  20. package/agents/gsd-eval-auditor.md +1 -1
  21. package/agents/gsd-executor.md +7 -3
  22. package/agents/gsd-integration-checker.compact.md +1 -1
  23. package/agents/gsd-integration-checker.md +1 -1
  24. package/agents/gsd-nyquist-auditor.compact.md +1 -1
  25. package/agents/gsd-nyquist-auditor.md +1 -1
  26. package/agents/gsd-phase-researcher.md +1 -1
  27. package/agents/gsd-plan-checker.md +2 -1
  28. package/agents/gsd-planner.md +5 -4
  29. package/agents/gsd-project-researcher.compact.md +1 -1
  30. package/agents/gsd-project-researcher.md +1 -1
  31. package/agents/gsd-research-synthesizer.compact.md +5 -5
  32. package/agents/gsd-research-synthesizer.md +5 -5
  33. package/agents/gsd-roadmapper.compact.md +1 -1
  34. package/agents/gsd-roadmapper.md +1 -1
  35. package/agents/gsd-security-auditor.compact.md +1 -1
  36. package/agents/gsd-security-auditor.md +1 -1
  37. package/agents/gsd-ui-auditor.compact.md +1 -1
  38. package/agents/gsd-ui-auditor.md +1 -1
  39. package/agents/gsd-ui-checker.compact.md +1 -1
  40. package/agents/gsd-ui-checker.md +1 -1
  41. package/agents/gsd-ui-researcher.compact.md +13 -1
  42. package/agents/gsd-ui-researcher.md +28 -1
  43. package/agents/gsd-verifier.md +2 -2
  44. package/bin/install.js +460 -105
  45. package/commands/gsd/code-review.md +8 -0
  46. package/commands/gsd/pause-work.md +1 -1
  47. package/gsd-core/bin/gsd-tools.cjs +124 -18
  48. package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
  49. package/gsd-core/bin/lib/args-terminator.cjs +114 -0
  50. package/gsd-core/bin/lib/broken-windows.cjs +59 -1
  51. package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
  52. package/gsd-core/bin/lib/capability-lock.cjs +41 -8
  53. package/gsd-core/bin/lib/capability-registry.cjs +162 -74
  54. package/gsd-core/bin/lib/capability-source.cjs +3 -1
  55. package/gsd-core/bin/lib/capability-state.cjs +20 -11
  56. package/gsd-core/bin/lib/capability-validator.cjs +23 -4
  57. package/gsd-core/bin/lib/commands.cjs +5 -1
  58. package/gsd-core/bin/lib/config-loader.cjs +13 -3
  59. package/gsd-core/bin/lib/config.cjs +256 -58
  60. package/gsd-core/bin/lib/core-utils.cjs +34 -0
  61. package/gsd-core/bin/lib/decision-coverage-support.cjs +93 -0
  62. package/gsd-core/bin/lib/gate-context-drift.cjs +8 -3
  63. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +21 -1
  64. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +19 -1
  65. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
  66. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
  67. package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
  68. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +114 -18
  69. package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
  70. package/gsd-core/bin/lib/git-info-exclude.cjs +57 -0
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +18 -3
  72. package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
  73. package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
  74. package/gsd-core/bin/lib/host-integration.cjs +13 -6
  75. package/gsd-core/bin/lib/init.cjs +76 -5
  76. package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
  77. package/gsd-core/bin/lib/install-engine.cjs +246 -32
  78. package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
  79. package/gsd-core/bin/lib/install-profiles.cjs +14 -7
  80. package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
  81. package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
  82. package/gsd-core/bin/lib/markdown-sectionizer.cjs +113 -0
  83. package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
  84. package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
  85. package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
  86. package/gsd-core/bin/lib/milestone.cjs +43 -1
  87. package/gsd-core/bin/lib/model-catalog.cjs +8 -0
  88. package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
  89. package/gsd-core/bin/lib/phase-id.cjs +21 -4
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +37 -4
  91. package/gsd-core/bin/lib/phase.cjs +235 -37
  92. package/gsd-core/bin/lib/planning-snapshot.cjs +3 -1
  93. package/gsd-core/bin/lib/planning-workspace.cjs +18 -0
  94. package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
  95. package/gsd-core/bin/lib/read-only-git-env.cjs +58 -0
  96. package/gsd-core/bin/lib/report-parser.cjs +57 -1
  97. package/gsd-core/bin/lib/review-lane-descriptor.cjs +35 -2
  98. package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
  99. package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
  100. package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
  101. package/gsd-core/bin/lib/roadmap-parser.cjs +12 -0
  102. package/gsd-core/bin/lib/roadmap-upgrade.cjs +39 -2
  103. package/gsd-core/bin/lib/roadmap.cjs +89 -30
  104. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +428 -16
  105. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
  106. package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
  107. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
  108. package/gsd-core/bin/lib/shell-command-projection.cjs +123 -16
  109. package/gsd-core/bin/lib/smart-entry.cjs +37 -16
  110. package/gsd-core/bin/lib/state-contract.cjs +29 -9
  111. package/gsd-core/bin/lib/state-document.cjs +47 -5
  112. package/gsd-core/bin/lib/state-transition.cjs +150 -34
  113. package/gsd-core/bin/lib/state.cjs +140 -88
  114. package/gsd-core/bin/lib/surface.cjs +9 -0
  115. package/gsd-core/bin/lib/task-command-router.cjs +7 -28
  116. package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
  117. package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
  118. package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
  119. package/gsd-core/bin/lib/verification.cjs +96 -9
  120. package/gsd-core/bin/lib/verify.cjs +38 -1
  121. package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
  122. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  123. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  124. package/gsd-core/references/agent-contracts.md +1 -1
  125. package/gsd-core/references/agent-skills-bootstrap.md +26 -7
  126. package/gsd-core/references/git-integration.md +14 -3
  127. package/gsd-core/references/loop-hook-dispatch.md +13 -6
  128. package/gsd-core/references/planner-reversibility.md +18 -4
  129. package/gsd-core/references/planner-verify-command-grounding.md +18 -0
  130. package/gsd-core/references/planning-config.md +2 -0
  131. package/gsd-core/references/tdd.md +5 -4
  132. package/gsd-core/references/verifier-phase-gates.md +8 -1
  133. package/gsd-core/templates/DEBUG.md +16 -0
  134. package/gsd-core/templates/config.json +2 -1
  135. package/gsd-core/templates/verification-report.md +1 -1
  136. package/gsd-core/workflows/add-backlog.md +10 -2
  137. package/gsd-core/workflows/add-phase.md +5 -1
  138. package/gsd-core/workflows/add-tests.md +5 -1
  139. package/gsd-core/workflows/add-todo.md +5 -1
  140. package/gsd-core/workflows/ai-integration-phase.md +5 -1
  141. package/gsd-core/workflows/audit-fix.md +2 -1
  142. package/gsd-core/workflows/audit-milestone.md +21 -9
  143. package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
  144. package/gsd-core/workflows/autonomous.md +52 -64
  145. package/gsd-core/workflows/check-todos.md +5 -1
  146. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
  147. package/gsd-core/workflows/code-review-fix.md +5 -1
  148. package/gsd-core/workflows/code-review.md +66 -8
  149. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
  150. package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
  151. package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
  152. package/gsd-core/workflows/complete-milestone.md +64 -157
  153. package/gsd-core/workflows/debug.md +5 -1
  154. package/gsd-core/workflows/diagnose-issues.md +34 -6
  155. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
  156. package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
  157. package/gsd-core/workflows/discuss-phase-assumptions.md +44 -5
  158. package/gsd-core/workflows/discuss-phase-power.md +19 -2
  159. package/gsd-core/workflows/discuss-phase.md +11 -11
  160. package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
  161. package/gsd-core/workflows/docs-update.md +27 -5
  162. package/gsd-core/workflows/edit-phase.md +5 -1
  163. package/gsd-core/workflows/eval-review.md +11 -3
  164. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
  165. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
  166. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +19 -8
  167. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +21 -3
  168. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
  169. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +7 -3
  170. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
  171. package/gsd-core/workflows/execute-phase.md +46 -11
  172. package/gsd-core/workflows/execute-plan.md +8 -4
  173. package/gsd-core/workflows/extract-learnings.md +6 -2
  174. package/gsd-core/workflows/forensics.md +5 -1
  175. package/gsd-core/workflows/health.md +2 -1
  176. package/gsd-core/workflows/import.md +6 -2
  177. package/gsd-core/workflows/insert-phase.md +5 -1
  178. package/gsd-core/workflows/manager.md +10 -2
  179. package/gsd-core/workflows/map-codebase.md +5 -1
  180. package/gsd-core/workflows/milestone-summary.md +10 -2
  181. package/gsd-core/workflows/new-milestone.md +13 -5
  182. package/gsd-core/workflows/new-project.md +9 -5
  183. package/gsd-core/workflows/next.md +8 -4
  184. package/gsd-core/workflows/pause-work.md +3 -3
  185. package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
  186. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +7 -3
  187. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
  188. package/gsd-core/workflows/plan-phase.md +54 -86
  189. package/gsd-core/workflows/plan-review-convergence.md +17 -5
  190. package/gsd-core/workflows/plant-seed.md +8 -4
  191. package/gsd-core/workflows/progress.md +6 -2
  192. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
  193. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
  194. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
  195. package/gsd-core/workflows/quick-batch.md +5 -1
  196. package/gsd-core/workflows/quick.md +23 -8
  197. package/gsd-core/workflows/remove-phase.md +5 -1
  198. package/gsd-core/workflows/resume-project.md +5 -1
  199. package/gsd-core/workflows/review.md +22 -15
  200. package/gsd-core/workflows/scan.md +5 -1
  201. package/gsd-core/workflows/section-manifest.json +5 -0
  202. package/gsd-core/workflows/secure-phase.md +6 -2
  203. package/gsd-core/workflows/settings.md +4 -3
  204. package/gsd-core/workflows/ship.md +16 -14
  205. package/gsd-core/workflows/spec-phase.md +3 -6
  206. package/gsd-core/workflows/thread.md +10 -2
  207. package/gsd-core/workflows/transition.md +5 -1
  208. package/gsd-core/workflows/ui-phase.md +5 -1
  209. package/gsd-core/workflows/ui-review.md +6 -2
  210. package/gsd-core/workflows/ultraplan-phase.md +5 -1
  211. package/gsd-core/workflows/update.md +5 -1
  212. package/gsd-core/workflows/validate-phase.md +6 -2
  213. package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
  214. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
  215. package/gsd-core/workflows/verify-work.md +15 -6
  216. package/hooks/dist/gsd-agent-isolation-guard.js +14 -2
  217. package/hooks/dist/gsd-cursor-subagent-start.js +8 -3
  218. package/hooks/dist/gsd-path-export.js +95 -0
  219. package/hooks/dist/gsd-secret-read-guard.js +244 -53
  220. package/hooks/dist/gsd-statusline.js +35 -2
  221. package/hooks/dist/gsd-validate-commit.sh +27 -5
  222. package/hooks/dist/gsd-windsurf-pre-write.js +28 -1
  223. package/hooks/dist/gsd-workflow-guard.js +1 -1
  224. package/hooks/dist/gsd-worktree-path-guard.js +28 -1
  225. package/hooks/dist/lib/isolation-sentinel.js +44 -0
  226. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  227. package/hooks/gsd-agent-isolation-guard.js +14 -2
  228. package/hooks/gsd-cursor-subagent-start.js +8 -3
  229. package/hooks/gsd-path-export.js +95 -0
  230. package/hooks/gsd-secret-read-guard.js +244 -53
  231. package/hooks/gsd-statusline.js +35 -2
  232. package/hooks/gsd-validate-commit.sh +27 -5
  233. package/hooks/gsd-windsurf-pre-write.js +28 -1
  234. package/hooks/gsd-workflow-guard.js +1 -1
  235. package/hooks/gsd-worktree-path-guard.js +28 -1
  236. package/hooks/hooks.json +1 -0
  237. package/hooks/lib/isolation-sentinel.js +44 -0
  238. package/hooks/managed-hooks-registry.cjs +1 -0
  239. package/package.json +13 -6
  240. package/pi/gsd.cjs +1047 -10
  241. package/pi/package-agents/gsd-advisor-researcher.md +113 -0
  242. package/pi/package-agents/gsd-ai-researcher.md +111 -0
  243. package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
  244. package/pi/package-agents/gsd-code-fixer.md +769 -0
  245. package/pi/package-agents/gsd-code-reviewer.md +402 -0
  246. package/pi/package-agents/gsd-codebase-mapper.md +850 -0
  247. package/pi/package-agents/gsd-debug-session-manager.md +416 -0
  248. package/pi/package-agents/gsd-debugger.md +1274 -0
  249. package/pi/package-agents/gsd-doc-classifier.md +269 -0
  250. package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
  251. package/pi/package-agents/gsd-doc-verifier.md +212 -0
  252. package/pi/package-agents/gsd-doc-writer.md +626 -0
  253. package/pi/package-agents/gsd-dom-verifier.md +164 -0
  254. package/pi/package-agents/gsd-domain-researcher.md +144 -0
  255. package/pi/package-agents/gsd-eval-auditor.md +185 -0
  256. package/pi/package-agents/gsd-eval-planner.md +149 -0
  257. package/pi/package-agents/gsd-executor.md +894 -0
  258. package/pi/package-agents/gsd-framework-selector.md +159 -0
  259. package/pi/package-agents/gsd-integration-checker.md +476 -0
  260. package/pi/package-agents/gsd-intel-updater.md +338 -0
  261. package/pi/package-agents/gsd-mempalace-curator.md +49 -0
  262. package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
  263. package/pi/package-agents/gsd-pattern-mapper.md +340 -0
  264. package/pi/package-agents/gsd-phase-researcher.md +897 -0
  265. package/pi/package-agents/gsd-plan-checker.md +1077 -0
  266. package/pi/package-agents/gsd-planner.md +1003 -0
  267. package/pi/package-agents/gsd-project-researcher.md +611 -0
  268. package/pi/package-agents/gsd-research-synthesizer.md +259 -0
  269. package/pi/package-agents/gsd-roadmapper.md +779 -0
  270. package/pi/package-agents/gsd-security-auditor.md +171 -0
  271. package/pi/package-agents/gsd-ui-auditor.md +581 -0
  272. package/pi/package-agents/gsd-ui-checker.md +417 -0
  273. package/pi/package-agents/gsd-ui-researcher.md +469 -0
  274. package/pi/package-agents/gsd-user-profiler.md +174 -0
  275. package/pi/package-agents/gsd-verifier.md +963 -0
  276. package/pi/package-agents/manifest.json +39 -0
  277. package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
  278. package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
  279. package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
  280. package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
  281. package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
  282. package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
  283. package/pi/package-skills/gsd-capture/SKILL.md +68 -0
  284. package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
  285. package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
  286. package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
  287. package/pi/package-skills/gsd-config/SKILL.md +60 -0
  288. package/pi/package-skills/gsd-debug/SKILL.md +64 -0
  289. package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
  290. package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
  291. package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
  292. package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
  293. package/pi/package-skills/gsd-explore/SKILL.md +26 -0
  294. package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
  295. package/pi/package-skills/gsd-fast/SKILL.md +33 -0
  296. package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
  297. package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
  298. package/pi/package-skills/gsd-health/SKILL.md +36 -0
  299. package/pi/package-skills/gsd-help/SKILL.md +37 -0
  300. package/pi/package-skills/gsd-import/SKILL.md +46 -0
  301. package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
  302. package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
  303. package/pi/package-skills/gsd-manager/SKILL.md +46 -0
  304. package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
  305. package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
  306. package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
  307. package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
  308. package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
  309. package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
  310. package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
  311. package/pi/package-skills/gsd-next/SKILL.md +34 -0
  312. package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
  313. package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
  314. package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
  315. package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
  316. package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
  317. package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
  318. package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
  319. package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
  320. package/pi/package-skills/gsd-phase/SKILL.md +61 -0
  321. package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
  322. package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
  323. package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
  324. package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
  325. package/pi/package-skills/gsd-progress/SKILL.md +51 -0
  326. package/pi/package-skills/gsd-quick/SKILL.md +179 -0
  327. package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
  328. package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
  329. package/pi/package-skills/gsd-review/SKILL.md +50 -0
  330. package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
  331. package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
  332. package/pi/package-skills/gsd-settings/SKILL.md +30 -0
  333. package/pi/package-skills/gsd-ship/SKILL.md +27 -0
  334. package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
  335. package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
  336. package/pi/package-skills/gsd-spike/SKILL.md +53 -0
  337. package/pi/package-skills/gsd-stats/SKILL.md +22 -0
  338. package/pi/package-skills/gsd-surface/SKILL.md +177 -0
  339. package/pi/package-skills/gsd-thread/SKILL.md +29 -0
  340. package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
  341. package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
  342. package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
  343. package/pi/package-skills/gsd-undo/SKILL.md +38 -0
  344. package/pi/package-skills/gsd-update/SKILL.md +51 -0
  345. package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
  346. package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
  347. package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
  348. package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
  349. package/scripts/audit-workflow-script-paths.cjs +1 -1
  350. package/scripts/build-hooks.js +4 -0
  351. package/scripts/command-contract-helpers.cjs +1 -1
  352. package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
  353. package/scripts/docs-guard-registry.cjs +22 -7
  354. package/scripts/gen-health-docs.cjs +13 -1
  355. package/scripts/gen-pi-package-skills.cjs +217 -0
  356. package/scripts/lib/macos-conformance-tier.generated.cjs +19 -0
  357. package/scripts/lib/platform-conformance-tier.generated.cjs +41 -0
  358. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
  359. package/scripts/lint-phase-arg-assignment.cjs +2 -2
  360. package/scripts/lint-retired-runtime-name.cjs +5 -0
  361. package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
  362. package/scripts/npm-audit-baseline.cjs +41 -7
  363. package/scripts/release-tarball-smoke.cjs +1 -0
  364. package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
  365. package/scripts/select-docs-guards.cjs +1 -1
  366. package/scripts/self-test-report-policy.cjs +350 -0
  367. package/skills/gsd-code-review/SKILL.md +8 -0
  368. package/skills/gsd-pause-work/SKILL.md +1 -1
  369. package/vscode/package.json +1 -1
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * "Reviewed or maintainer" policy (#5607).
6
+ *
7
+ * A pull request passes when EITHER
8
+ * 1. its author is listed in .github/MAINTAINERS (a maintainer's own PR needs
9
+ * no second reviewer), OR
10
+ * 2. someone other than the author, holding write/maintain/admin permission,
11
+ * has an APPROVED review on the CURRENT head sha as their latest standing
12
+ * review.
13
+ *
14
+ * Why this is a check and not a ruleset review count: a ruleset's
15
+ * required_approving_review_count is one number for every author, so it cannot
16
+ * say "required unless the author is a maintainer". The sole maintainer cannot
17
+ * approve their own PR, which is what pushed merges onto `--admin` (#4990).
18
+ *
19
+ * Everything that could wrongly COUNT an approval fails closed here: a stale
20
+ * sha, the author's own review, a reviewer without write access, a later
21
+ * dismissal or change request by the same reviewer, a truncated review list, an
22
+ * unreadable maintainer file. Wrongly FAILING only asks a maintainer for one
23
+ * more review.
24
+ *
25
+ * This module is pure. The workflow gathers reviews and roles with `gh api` and
26
+ * hands them in as files; the policy and the maintainer list are read from the
27
+ * BASE branch, never from the PR (.github/workflows/reviewed-or-maintainer.yml).
28
+ */
29
+
30
+ const fs = require('node:fs');
31
+ const { runMain } = require('./lib/cli-exit.cjs');
32
+
33
+ const REASON = Object.freeze({
34
+ OK_MAINTAINER_AUTHOR: 'ok_maintainer_author',
35
+ OK_APPROVED: 'ok_approved',
36
+ FAIL_NO_APPROVAL: 'fail_no_approval',
37
+ FAIL_STALE_APPROVAL_ONLY: 'fail_stale_approval_only',
38
+ FAIL_MAINTAINERS_UNREADABLE: 'fail_maintainers_unreadable',
39
+ FAIL_REVIEWS_INCOMPLETE: 'fail_reviews_incomplete',
40
+ FAIL_INPUT_INVALID: 'fail_input_invalid',
41
+ });
42
+
43
+ // Permission levels allowed to give a qualifying approval. GitHub's
44
+ // `role_name` for collaborators: read, triage, write, maintain, admin.
45
+ const APPROVER_ROLES = new Set(['write', 'maintain', 'admin']);
46
+
47
+ // A GitHub login: alphanumerics and single hyphens, 1-39 chars, optional
48
+ // `[bot]` suffix. Anything else on a MAINTAINERS line is reported, not trusted.
49
+ const LOGIN_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\[bot\])?$/;
50
+ const SHA_RE = /^[0-9a-f]{40}$/i;
51
+
52
+ // Review states that change a reviewer's standing. COMMENTED and PENDING do
53
+ // not: a later comment does not withdraw an approval on GitHub either.
54
+ const STANDING_STATES = new Set(['APPROVED', 'CHANGES_REQUESTED', 'DISMISSED']);
55
+
56
+ /**
57
+ * @param {unknown} text - contents of .github/MAINTAINERS
58
+ * @returns {{ok: boolean, logins: Set<string>, invalid: string[]}}
59
+ */
60
+ function parseMaintainers(text) {
61
+ const logins = new Set();
62
+ const invalid = [];
63
+ if (typeof text !== 'string') return { ok: false, logins, invalid };
64
+ for (const raw of text.split(/\r?\n/)) {
65
+ const line = raw.replace(/#.*$/, '').trim();
66
+ if (line === '') continue;
67
+ if (LOGIN_RE.test(line)) logins.add(line.toLowerCase());
68
+ else invalid.push(line);
69
+ }
70
+ return { ok: logins.size > 0, logins, invalid };
71
+ }
72
+
73
+ function isBot(entry) {
74
+ const user = entry && entry.user;
75
+ return Boolean(user && (user.type === 'Bot' || /\[bot\]$/i.test(String(user.login || ''))));
76
+ }
77
+
78
+ function lower(value) {
79
+ return typeof value === 'string' ? value.toLowerCase() : '';
80
+ }
81
+
82
+ /**
83
+ * @param {object} input
84
+ * @param {string} input.authorLogin
85
+ * @param {Set<string>} input.maintainers - lowercase logins
86
+ * @param {Array<{user: {login: string}, state: string, commit_id: string}>} input.reviews - chronological
87
+ * @param {Record<string, string>} input.roles - lowercase login -> permission role name
88
+ * @param {string} input.headSha - 40 hex
89
+ * @param {boolean} input.reviewsComplete - false when the review list may be truncated
90
+ * @returns {{ok: boolean, reason: string}}
91
+ */
92
+ function evaluateReviewed({ authorLogin, maintainers, reviews, roles, headSha, reviewsComplete }) {
93
+ if (typeof authorLogin !== 'string' || authorLogin === '') {
94
+ return { ok: false, reason: REASON.FAIL_INPUT_INVALID };
95
+ }
96
+ if (typeof headSha !== 'string' || !SHA_RE.test(headSha)) {
97
+ return { ok: false, reason: REASON.FAIL_INPUT_INVALID };
98
+ }
99
+ if (!(maintainers instanceof Set) || maintainers.size === 0) {
100
+ return { ok: false, reason: REASON.FAIL_MAINTAINERS_UNREADABLE };
101
+ }
102
+
103
+ const author = lower(authorLogin);
104
+ if (maintainers.has(author)) return { ok: true, reason: REASON.OK_MAINTAINER_AUTHOR };
105
+
106
+ if (!Array.isArray(reviews)) return { ok: false, reason: REASON.FAIL_INPUT_INVALID };
107
+ if (reviewsComplete !== true) return { ok: false, reason: REASON.FAIL_REVIEWS_INCOMPLETE };
108
+
109
+ // Latest standing review per reviewer (input is chronological).
110
+ const standing = new Map();
111
+ for (const entry of reviews) {
112
+ const login = lower(entry && entry.user && entry.user.login);
113
+ if (login === '' || !STANDING_STATES.has(entry.state)) continue;
114
+ standing.set(login, entry);
115
+ }
116
+
117
+ let sawStaleApproval = false;
118
+ for (const [login, entry] of standing) {
119
+ if (login === author) continue;
120
+ if (entry.state !== 'APPROVED') continue;
121
+ // An app or bot (for example github-actions[bot] when Actions may approve
122
+ // PRs) is not a human review, whatever permission it holds.
123
+ if (isBot(entry)) continue;
124
+ const role = roles && typeof roles === 'object' ? roles[login] : undefined;
125
+ if (!APPROVER_ROLES.has(role)) continue;
126
+ if (lower(entry.commit_id) === lower(headSha)) return { ok: true, reason: REASON.OK_APPROVED };
127
+ sawStaleApproval = true;
128
+ }
129
+
130
+ return {
131
+ ok: false,
132
+ reason: sawStaleApproval ? REASON.FAIL_STALE_APPROVAL_ONLY : REASON.FAIL_NO_APPROVAL,
133
+ };
134
+ }
135
+
136
+ function readJson(file) {
137
+ try {
138
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
139
+ } catch {
140
+ return undefined;
141
+ }
142
+ }
143
+
144
+ function main() {
145
+ const maintainersText = (() => {
146
+ try {
147
+ return fs.readFileSync(process.env.MAINTAINERS_FILE || '.github/MAINTAINERS', 'utf8');
148
+ } catch {
149
+ return undefined;
150
+ }
151
+ })();
152
+ const parsed = parseMaintainers(maintainersText);
153
+ for (const line of parsed.invalid) {
154
+ process.stderr.write(`::warning::MAINTAINERS: ignored malformed line: ${JSON.stringify(line)}\n`);
155
+ }
156
+
157
+ const reviews = process.env.REVIEWS_FILE ? readJson(process.env.REVIEWS_FILE) : undefined;
158
+ const roles = process.env.ROLES_FILE ? readJson(process.env.ROLES_FILE) : undefined;
159
+
160
+ const result = evaluateReviewed({
161
+ authorLogin: process.env.PR_AUTHOR_LOGIN || '',
162
+ maintainers: parsed.ok ? parsed.logins : null,
163
+ reviews,
164
+ roles: roles && typeof roles === 'object' ? roles : {},
165
+ headSha: process.env.HEAD_SHA || '',
166
+ // The workflow's `gh api --paginate` either returns every page or exits
167
+ // non-zero before this runs; it states completeness explicitly.
168
+ reviewsComplete: process.env.REVIEWS_COMPLETE === 'true',
169
+ });
170
+
171
+ process.stdout.write(`${JSON.stringify(result)}\n`);
172
+ if (process.env.GITHUB_OUTPUT) {
173
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `ok=${result.ok ? 'true' : 'false'}\n`);
174
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `reason=${result.reason}\n`);
175
+ }
176
+ return result.ok ? 0 : 1;
177
+ }
178
+
179
+ if (require.main === module) runMain(main);
180
+
181
+ module.exports = { REASON, APPROVER_ROLES, parseMaintainers, evaluateReviewed };
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  /**
25
- * @param {string} changedPath - a single changed docs/ path (e.g. 'docs/AGENTS.md').
25
+ * @param {string} changedPath - a single changed docs/ path (e.g. 'docs/reference/agents.md').
26
26
  * @param {string} pattern - one entry from a registry value array.
27
27
  * @returns {boolean}
28
28
  */
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Self-test report policy (#5612).
6
+ *
7
+ * A fix PR from someone who is not a listed maintainer must carry a
8
+ * `## Self-test report` section holding a fenced `json` block that records what
9
+ * the submitter ran: a failing-first test (fails on the base, passes on the
10
+ * head), the commands, the environment, and a passing full suite, bound to the
11
+ * PR's CURRENT head commit.
12
+ *
13
+ * TRUST MODEL. This validates shape and sha binding, not truth. A local run
14
+ * cannot be attested; CI on the merge-group commit stays authoritative. The
15
+ * report is a head start for the reviewer and a nudge on effort, nothing more.
16
+ * Every result here is advisory until a maintainer chooses to require it.
17
+ *
18
+ * Pure module: the workflow hands in the PR body, head ref, title, author, head
19
+ * sha and the maintainer list (read from the BASE sha, never the PR).
20
+ */
21
+
22
+ const fs = require('node:fs');
23
+ const { runMain } = require('./lib/cli-exit.cjs');
24
+ const { parseMaintainers } = require('./reviewed-or-maintainer-policy.cjs');
25
+
26
+ const REASON = Object.freeze({
27
+ OK_NOT_A_FIX: 'ok_not_a_fix',
28
+ OK_MAINTAINER_AUTHOR: 'ok_maintainer_author',
29
+ OK_BOT_AUTHOR: 'ok_bot_author',
30
+ OK_REPORT: 'ok_report',
31
+ FAIL_NO_REPORT: 'fail_no_report',
32
+ FAIL_MALFORMED_JSON: 'fail_malformed_json',
33
+ FAIL_SCHEMA: 'fail_schema',
34
+ FAIL_STALE_SHA: 'fail_stale_sha',
35
+ FAIL_NOT_FAILING_FIRST: 'fail_not_failing_first',
36
+ FAIL_SUITE_NOT_PASSED: 'fail_suite_not_passed',
37
+ FAIL_MAINTAINERS_UNREADABLE: 'fail_maintainers_unreadable',
38
+ FAIL_INPUT_INVALID: 'fail_input_invalid',
39
+ });
40
+
41
+ const SCHEMA_VERSION = 1;
42
+ const TOOLS = new Set(['gsd-test', 'npm-test', 'manual']);
43
+ const SUITE_RESULTS = new Set(['passed', 'failed', 'skipped']);
44
+ const SHA_RE = /^[0-9a-f]{40}$/i;
45
+ const PLACEHOLDER_RE = /^<[^<>]*>$/;
46
+ const EXCERPT_MAX = 2000;
47
+ const SECTION_TITLE = 'self-test report';
48
+
49
+ /** A PR is a "fix PR" when its head ref is fix/… or its title is a fix. */
50
+ function isFixPr({ headRef, title }) {
51
+ if (typeof headRef === 'string' && headRef.startsWith('fix/')) return true;
52
+ return typeof title === 'string' && /^fix(\([^)]*\))?!?:/i.test(title.trim());
53
+ }
54
+
55
+ function isBotLogin(login) {
56
+ return typeof login === 'string' && /\[bot\]$/i.test(login);
57
+ }
58
+
59
+ /**
60
+ * Find the first fenced ```json block inside the `## Self-test report` section.
61
+ *
62
+ * - Fence-aware: a `#` line inside a fence is not a heading. A fence may carry
63
+ * trailing info after the language (```json title="x"); only its first word
64
+ * names the language. A closing fence has no info and is at least as long as
65
+ * the opener, with the same character.
66
+ * - HTML comments are skipped outside fences, so a block hidden in a comment
67
+ * (invisible in the rendered PR) is not the report.
68
+ * - The section ends at the next heading of the same or a higher level.
69
+ * - The first json block wins.
70
+ *
71
+ * @returns {{found: boolean, json: string|null}}
72
+ */
73
+ // Lines longer than this are not fence or heading syntax. Capping what the
74
+ // regexes see keeps a crafted line of whitespace from costing more than a bounded
75
+ // amount of work (the body is attacker-controlled); captured JSON keeps its full text.
76
+ const SYNTAX_LINE_MAX = 1000;
77
+
78
+ /**
79
+ * ATX heading → {level, text}, parsed without a backtracking regex: a body line
80
+ * is attacker input and the obvious `^(#+)\s+(.*?)\s*#*\s*$` is cubic on a long
81
+ * run of spaces. A trailing run of `#` is a closing sequence only when preceded
82
+ * by whitespace (or the text is only hashes).
83
+ */
84
+ function parseHeading(line) {
85
+ const trimmed = line.trim();
86
+ let level = 0;
87
+ while (level < trimmed.length && trimmed[level] === '#') level += 1;
88
+ if (level < 1 || level > 6) return null;
89
+ if (level >= trimmed.length || !/\s/.test(trimmed[level])) return null;
90
+ let text = trimmed.slice(level).trim();
91
+ let end = text.length;
92
+ while (end > 0 && text[end - 1] === '#') end -= 1;
93
+ if (end < text.length && (end === 0 || /\s/.test(text[end - 1]))) text = text.slice(0, end).trimEnd();
94
+ return { level, text: text.replace(/\*\*/g, '').trim().toLowerCase() };
95
+ }
96
+
97
+ function extractReport(body) {
98
+ const lines = String(body || '').split(/\r?\n/);
99
+ let inFence = false;
100
+ let fenceChar = '';
101
+ let fenceLen = 0;
102
+ let inComment = false;
103
+ let sectionLevel = 0;
104
+ let capturing = false;
105
+ let captured = [];
106
+
107
+ for (const full of lines) {
108
+ const line = full.length > SYNTAX_LINE_MAX ? full.slice(0, SYNTAX_LINE_MAX) : full;
109
+ if (!inFence) {
110
+ if (inComment) {
111
+ if (line.includes('-->')) inComment = false;
112
+ continue;
113
+ }
114
+ const commentAt = line.indexOf('<!--');
115
+ if (commentAt !== -1 && line.indexOf('-->', commentAt + 4) === -1) {
116
+ inComment = true;
117
+ continue;
118
+ }
119
+ }
120
+
121
+ if (!inFence) {
122
+ const open = /^\s*(`{3,}|~{3,})\s*([^\s`]*)(?:\s.*)?$/.exec(line);
123
+ if (open) {
124
+ if (sectionLevel > 0 && !capturing && open[2].toLowerCase() === 'json') {
125
+ capturing = true;
126
+ captured = [];
127
+ }
128
+ inFence = true;
129
+ fenceChar = open[1][0];
130
+ fenceLen = open[1].length;
131
+ continue;
132
+ }
133
+ } else {
134
+ const close = /^\s*(`{3,}|~{3,})\s*$/.exec(line);
135
+ if (close && close[1][0] === fenceChar && close[1].length >= fenceLen) {
136
+ inFence = false;
137
+ if (capturing) return { found: true, json: captured.join('\n') };
138
+ continue;
139
+ }
140
+ if (capturing) captured.push(full);
141
+ continue;
142
+ }
143
+
144
+ const heading = parseHeading(line);
145
+ if (heading) {
146
+ if (sectionLevel === 0) {
147
+ if (heading.text === SECTION_TITLE) sectionLevel = heading.level;
148
+ } else if (heading.level <= sectionLevel) {
149
+ return { found: false, json: null };
150
+ }
151
+ }
152
+ }
153
+ return { found: false, json: null };
154
+ }
155
+
156
+ function isNonEmptyString(value) {
157
+ return typeof value === 'string' && value.trim() !== '';
158
+ }
159
+
160
+ function isCount(value) {
161
+ return Number.isInteger(value) && value >= 0;
162
+ }
163
+
164
+ /** Names of report fields whose string value is still an unfilled `<…>` placeholder. */
165
+ function placeholderFields(report) {
166
+ const found = [];
167
+ const check = (name, value) => {
168
+ if (typeof value === 'string' && PLACEHOLDER_RE.test(value.trim())) found.push(name);
169
+ };
170
+ check('head_sha', report.head_sha);
171
+ check('output_excerpt', report.output_excerpt);
172
+ if (report.failing_first && typeof report.failing_first === 'object') {
173
+ check('failing_first.test', report.failing_first.test);
174
+ check('failing_first.not_applicable_reason', report.failing_first.not_applicable_reason);
175
+ }
176
+ if (Array.isArray(report.commands)) report.commands.forEach((c) => check('commands', c));
177
+ if (report.full_suite && typeof report.full_suite === 'object') check('full_suite.command', report.full_suite.command);
178
+ if (report.environment && typeof report.environment === 'object') {
179
+ check('environment.os', report.environment.os);
180
+ check('environment.node', report.environment.node);
181
+ }
182
+ return [...new Set(found)];
183
+ }
184
+
185
+ /**
186
+ * @param {unknown} report - parsed JSON
187
+ * @param {string} headSha
188
+ * @returns {{ok: boolean, reason: string, problems: string[]}}
189
+ */
190
+ function validateReport(report, headSha) {
191
+ const problems = [];
192
+ const fail = (reason) => ({ ok: false, reason, problems });
193
+
194
+ if (!report || typeof report !== 'object' || Array.isArray(report)) {
195
+ problems.push('the report must be a JSON object');
196
+ return fail(REASON.FAIL_SCHEMA);
197
+ }
198
+ if (report.schema !== SCHEMA_VERSION) problems.push(`schema must be ${SCHEMA_VERSION}`);
199
+ if (typeof report.head_sha !== 'string' || !SHA_RE.test(report.head_sha)) {
200
+ problems.push('head_sha must be the 40-hex sha of your final commit');
201
+ }
202
+ if (!TOOLS.has(report.tool)) problems.push(`tool must be one of ${[...TOOLS].join(', ')}`);
203
+
204
+ const ff = report.failing_first;
205
+ if (!ff || typeof ff !== 'object' || Array.isArray(ff)) {
206
+ problems.push('failing_first must be an object');
207
+ } else if (ff.not_applicable_reason !== undefined) {
208
+ if (!isNonEmptyString(ff.not_applicable_reason)) {
209
+ problems.push('failing_first.not_applicable_reason must be a non-empty explanation');
210
+ }
211
+ } else {
212
+ if (!isNonEmptyString(ff.test)) problems.push('failing_first.test must name the test file');
213
+ if (typeof ff.fails_on_base !== 'boolean') problems.push('failing_first.fails_on_base must be true or false');
214
+ if (typeof ff.passes_on_head !== 'boolean') problems.push('failing_first.passes_on_head must be true or false');
215
+ }
216
+
217
+ if (!Array.isArray(report.commands) || report.commands.length === 0 || !report.commands.every(isNonEmptyString)) {
218
+ problems.push('commands must be a non-empty list of the commands you ran');
219
+ }
220
+
221
+ const suite = report.full_suite;
222
+ if (!suite || typeof suite !== 'object' || Array.isArray(suite)) {
223
+ problems.push('full_suite must be an object');
224
+ } else {
225
+ if (!isNonEmptyString(suite.command)) problems.push('full_suite.command must name the command');
226
+ if (!SUITE_RESULTS.has(suite.result)) problems.push(`full_suite.result must be one of ${[...SUITE_RESULTS].join(', ')}`);
227
+ if (!isCount(suite.passed)) problems.push('full_suite.passed must be a non-negative integer');
228
+ if (!isCount(suite.failed)) problems.push('full_suite.failed must be a non-negative integer');
229
+ }
230
+
231
+ const env = report.environment;
232
+ if (!env || typeof env !== 'object' || Array.isArray(env) || !isNonEmptyString(env.os) || !isNonEmptyString(env.node)) {
233
+ problems.push('environment must give os and node');
234
+ }
235
+
236
+ if (!isNonEmptyString(report.output_excerpt) || report.output_excerpt.length > EXCERPT_MAX) {
237
+ problems.push(`output_excerpt must be a non-empty excerpt of at most ${EXCERPT_MAX} characters`);
238
+ }
239
+
240
+ // A value still in `<…>` is the template, not something the submitter filled in.
241
+ for (const name of placeholderFields(report)) problems.push(`${name} is still the template placeholder; fill it in`);
242
+
243
+ if (problems.length > 0) return fail(REASON.FAIL_SCHEMA);
244
+
245
+ // Structurally sound. Now the substantive checks, most informative first.
246
+ if (report.head_sha.toLowerCase() !== String(headSha).toLowerCase()) {
247
+ problems.push(`head_sha ${report.head_sha} is not the PR's current head ${headSha}; update the report after your last push`);
248
+ return fail(REASON.FAIL_STALE_SHA);
249
+ }
250
+ if (ff.not_applicable_reason === undefined && !(ff.fails_on_base === true && ff.passes_on_head === true)) {
251
+ problems.push('the failing-first test must fail on the base and pass on your head');
252
+ return fail(REASON.FAIL_NOT_FAILING_FIRST);
253
+ }
254
+ if (suite.result !== 'passed' || suite.failed !== 0 || suite.passed < 1) {
255
+ problems.push('the full suite must have passed, with at least one passing test and no failures');
256
+ return fail(REASON.FAIL_SUITE_NOT_PASSED);
257
+ }
258
+ return { ok: true, reason: REASON.OK_REPORT, problems };
259
+ }
260
+
261
+ /**
262
+ * @param {object} input
263
+ * @param {string} input.body
264
+ * @param {string} input.headRef
265
+ * @param {string} input.title
266
+ * @param {string} input.authorLogin
267
+ * @param {string} input.headSha - 40 hex, read live
268
+ * @param {Set<string>|null} input.maintainers - lowercase logins
269
+ * @returns {{ok: boolean, reason: string, problems: string[]}}
270
+ */
271
+ function evaluateSelfTestReport({ body, headRef, title, authorLogin, headSha, maintainers }) {
272
+ const none = [];
273
+ if (typeof authorLogin !== 'string' || authorLogin === '' || typeof headSha !== 'string' || !SHA_RE.test(headSha)) {
274
+ return { ok: false, reason: REASON.FAIL_INPUT_INVALID, problems: none };
275
+ }
276
+ if (!isFixPr({ headRef, title })) return { ok: true, reason: REASON.OK_NOT_A_FIX, problems: none };
277
+ if (isBotLogin(authorLogin)) return { ok: true, reason: REASON.OK_BOT_AUTHOR, problems: none };
278
+ if (!(maintainers instanceof Set) || maintainers.size === 0) {
279
+ return { ok: false, reason: REASON.FAIL_MAINTAINERS_UNREADABLE, problems: none };
280
+ }
281
+ if (maintainers.has(authorLogin.toLowerCase())) {
282
+ return { ok: true, reason: REASON.OK_MAINTAINER_AUTHOR, problems: none };
283
+ }
284
+
285
+ const extracted = extractReport(body);
286
+ if (!extracted.found) {
287
+ return { ok: false, reason: REASON.FAIL_NO_REPORT, problems: ['no ```json block under a "## Self-test report" heading'] };
288
+ }
289
+ let parsed;
290
+ try {
291
+ parsed = JSON.parse(extracted.json);
292
+ } catch (error) {
293
+ return { ok: false, reason: REASON.FAIL_MALFORMED_JSON, problems: [`the report is not valid JSON: ${error.message}`] };
294
+ }
295
+ return validateReport(parsed, headSha);
296
+ }
297
+
298
+ /** A value from the environment, or from the file named by `<NAME>_FILE` (live reads are too big for env). */
299
+ function fromEnvOrFile(name) {
300
+ const file = process.env[`${name}_FILE`];
301
+ if (file) {
302
+ try {
303
+ return fs.readFileSync(file, 'utf8');
304
+ } catch {
305
+ return undefined;
306
+ }
307
+ }
308
+ return process.env[name] || '';
309
+ }
310
+
311
+ function main() {
312
+ let maintainersText;
313
+ try {
314
+ maintainersText = fs.readFileSync(process.env.MAINTAINERS_FILE || '.github/MAINTAINERS', 'utf8');
315
+ } catch {
316
+ maintainersText = undefined;
317
+ }
318
+ const parsed = parseMaintainers(maintainersText);
319
+
320
+ const body = fromEnvOrFile('PR_BODY');
321
+ const title = fromEnvOrFile('PR_TITLE');
322
+
323
+ const result = body === undefined || title === undefined
324
+ ? { ok: false, reason: REASON.FAIL_INPUT_INVALID, problems: [] }
325
+ : evaluateSelfTestReport({
326
+ body,
327
+ headRef: process.env.HEAD_REF || '',
328
+ title,
329
+ authorLogin: process.env.PR_AUTHOR_LOGIN || '',
330
+ headSha: process.env.HEAD_SHA || '',
331
+ maintainers: parsed.ok ? parsed.logins : null,
332
+ });
333
+
334
+ process.stdout.write(`${JSON.stringify(result)}\n`);
335
+ for (const problem of result.problems) {
336
+ // The annotation text is the validator's own wording plus, for a JSON syntax
337
+ // error, the parser's message (which can echo attacker text): strip newlines
338
+ // so no extra workflow command can start a line.
339
+ process.stderr.write(`::error::Self-test report: ${problem.replace(/[\r\n]+/g, ' ').slice(0, 300)}\n`);
340
+ }
341
+ if (process.env.GITHUB_OUTPUT) {
342
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `ok=${result.ok ? 'true' : 'false'}\n`);
343
+ fs.appendFileSync(process.env.GITHUB_OUTPUT, `reason=${result.reason}\n`);
344
+ }
345
+ return result.ok ? 0 : 1;
346
+ }
347
+
348
+ if (require.main === module) runMain(main);
349
+
350
+ module.exports = { REASON, SCHEMA_VERSION, EXCERPT_MAX, isFixPr, extractReport, validateReport, evaluateSelfTestReport };
@@ -48,6 +48,14 @@ Optional flags parsed from the `<arguments>` block:
48
48
  - `--files=file1,file2,...` — Explicit file list override. Has highest precedence for file scoping per D-08. When provided, workflow skips SUMMARY.md extraction and git diff fallback entirely.
49
49
 
50
50
  Context files (CLAUDE.md, SUMMARY.md, phase state) are resolved inside the workflow via `gsd-tools query init.phase-op` and delegated to agent via `<required_reading>` blocks.
51
+
52
+ ## Outside a phase
53
+
54
+ Reviewer lanes run without a phase (`gsd_run` is defined by the runtime-launcher preamble inside workflow fences; in a plain shell call `node <install>/gsd-core/bin/gsd-tools.cjs` instead): pipe canonical repo-relative file paths (one per line) to stdin of
55
+ `gsd_run review-lane dispatch-step --repo-root <root> --depth <quick|standard|deep> --base-sha <sha> --run-dir "$(mktemp -d)" --cap-id code-review --point execute:post --explicit <slugs> --raw`.
56
+ `review-lane` flags are space-separated (`--depth quick`), never `--flag=value`. `--explicit` (omitted, nothing dispatches yet the call exits ok), `--depth`, `--base-sha` (lane reviews changes relative to it; commit them first) and stdin paths are required; `--run-dir` should be a fresh temp dir (default `.` writes the prompt file into the current directory); read each lane's output path (`results[].reviewPath`) from the JSON before removing it. `--cap-id`/`--point` select the `code-review` capability, which must be enabled (`workflow.code_review`) and active at `--point` — `execute:post`, or `execute:wave:post` when `workflow.code_review_point` selects it; otherwise nothing dispatches.
57
+ `--explicit` takes comma-separated canonical slugs, no spaces (`codex,antigravity`), not flag aliases (`antigravity`, not `agy`); slugs: first column of `gsd_run review-lane sections` (`review-lane flags` lists flags/aliases).
58
+ Each lane declares its own model key, which is not always its slug (antigravity's is `review.models.agy`); see the `review.models.*` table in `docs/CONFIGURATION.md`. Unset = lane default model.
51
59
  </context>
52
60
 
53
61
  <process>
@@ -21,7 +21,7 @@ Routes to the pause-work workflow which handles:
21
21
  - Current phase detection from recent files
22
22
  - Complete state gathering (position, completed work, remaining work, decisions, blockers)
23
23
  - Handoff file creation with all context sections
24
- - Git commit as WIP
24
+ - Git commit handoff
25
25
  - Resume instructions
26
26
  </objective>
27
27
 
@@ -2,7 +2,7 @@
2
2
  "name": "gsd-core-vscode",
3
3
  "displayName": "GSD Core",
4
4
  "description": "GSD orchestration engine embedded in VS Code (ADR-1239 IDE profile).",
5
- "version": "1.17.0",
5
+ "version": "1.19.0",
6
6
  "publisher": "opengsd",
7
7
  "engines": {
8
8
  "vscode": "^1.105.0"