@opengsd/gsd-core 1.12.0 → 1.13.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 (286) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-executor.md +63 -35
  5. package/agents/gsd-plan-checker.md +76 -57
  6. package/agents/gsd-planner.md +14 -0
  7. package/agents/gsd-ui-checker.md +19 -3
  8. package/agents/gsd-ui-researcher.md +29 -0
  9. package/agents/gsd-verifier.md +23 -1
  10. package/bin/install.js +239 -67
  11. package/commands/gsd/execute-phase.md +1 -1
  12. package/commands/gsd/ns-workflow.md +2 -1
  13. package/commands/gsd/phase.md +1 -1
  14. package/commands/gsd/quick-batch.md +105 -0
  15. package/commands/gsd/surface.md +18 -8
  16. package/gsd-core/bin/gsd-tools.cjs +195 -50
  17. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  18. package/gsd-core/bin/lib/capability-registry.cjs +514 -114
  19. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  20. package/gsd-core/bin/lib/capability-validator.cjs +120 -4
  21. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  22. package/gsd-core/bin/lib/check-command-router.cjs +85 -2
  23. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  24. package/gsd-core/bin/lib/clusters.cjs +1 -0
  25. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  26. package/gsd-core/bin/lib/commands.cjs +337 -13
  27. package/gsd-core/bin/lib/config-loader.cjs +3 -0
  28. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  29. package/gsd-core/bin/lib/decisions.cjs +213 -1
  30. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  31. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  32. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  33. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  34. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  35. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  36. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  37. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  38. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  39. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  40. package/gsd-core/bin/lib/init.cjs +132 -15
  41. package/gsd-core/bin/lib/install-engine.cjs +184 -12
  42. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  43. package/gsd-core/bin/lib/install-profiles.cjs +22 -14
  44. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  45. package/gsd-core/bin/lib/io.cjs +35 -0
  46. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  47. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  48. package/gsd-core/bin/lib/milestone.cjs +22 -2
  49. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  50. package/gsd-core/bin/lib/phase-id.cjs +251 -9
  51. package/gsd-core/bin/lib/phase.cjs +774 -35
  52. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  53. package/gsd-core/bin/lib/planning-snapshot.cjs +147 -20
  54. package/gsd-core/bin/lib/planning-workspace.cjs +103 -28
  55. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  56. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  57. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  58. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  59. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  60. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  61. package/gsd-core/bin/lib/roadmap-parser.cjs +499 -26
  62. package/gsd-core/bin/lib/roadmap.cjs +187 -58
  63. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +233 -33
  64. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  65. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +286 -108
  66. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +215 -43
  67. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  68. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  69. package/gsd-core/bin/lib/state-document.cjs +30 -5
  70. package/gsd-core/bin/lib/state-md-schema.cjs +23 -13
  71. package/gsd-core/bin/lib/state-transition.cjs +333 -44
  72. package/gsd-core/bin/lib/state.cjs +684 -125
  73. package/gsd-core/bin/lib/surface.cjs +23 -8
  74. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  75. package/gsd-core/bin/lib/uat.cjs +1419 -515
  76. package/gsd-core/bin/lib/update-context.cjs +6 -2
  77. package/gsd-core/bin/lib/validate.cjs +230 -12
  78. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  79. package/gsd-core/bin/lib/verification.cjs +273 -12
  80. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  81. package/gsd-core/bin/lib/verify.cjs +346 -16
  82. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  83. package/gsd-core/bin/lib/worktree-safety.cjs +8 -0
  84. package/gsd-core/bin/shared/config-schema.manifest.json +8 -0
  85. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  86. package/gsd-core/references/agent-contracts.md +3 -3
  87. package/gsd-core/references/edge-probe.md +17 -13
  88. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  89. package/gsd-core/references/execute-phase-response-language.md +6 -0
  90. package/gsd-core/references/executor-examples.md +42 -0
  91. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  92. package/gsd-core/references/mvp-concepts.md +2 -2
  93. package/gsd-core/references/plan-checker-examples.md +41 -0
  94. package/gsd-core/references/planner-antipatterns.md +25 -0
  95. package/gsd-core/references/planner-chunked.md +5 -1
  96. package/gsd-core/references/planner-coupling.md +42 -0
  97. package/gsd-core/references/planner-quick-batch.md +71 -0
  98. package/gsd-core/references/planner-reviews.md +47 -0
  99. package/gsd-core/references/planner-revision.md +75 -2
  100. package/gsd-core/references/planning-config.md +2 -1
  101. package/gsd-core/references/response-language-directive.md +9 -0
  102. package/gsd-core/references/revision-loop.md +118 -11
  103. package/gsd-core/references/tdd.md +14 -9
  104. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  105. package/gsd-core/templates/phase-prompt.md +4 -0
  106. package/gsd-core/templates/verification-report.md +5 -0
  107. package/gsd-core/workflows/add-backlog.md +2 -0
  108. package/gsd-core/workflows/add-phase.md +2 -0
  109. package/gsd-core/workflows/add-tests.md +1 -1
  110. package/gsd-core/workflows/add-todo.md +1 -1
  111. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  112. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  113. package/gsd-core/workflows/audit-fix.md +2 -0
  114. package/gsd-core/workflows/audit-milestone.md +2 -0
  115. package/gsd-core/workflows/audit-uat.md +2 -0
  116. package/gsd-core/workflows/autonomous.md +2 -0
  117. package/gsd-core/workflows/check-todos.md +1 -1
  118. package/gsd-core/workflows/cleanup.md +1 -1
  119. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +15 -13
  120. package/gsd-core/workflows/code-review-fix.md +2 -0
  121. package/gsd-core/workflows/code-review.md +73 -31
  122. package/gsd-core/workflows/complete-milestone.md +13 -4
  123. package/gsd-core/workflows/debug.md +1 -1
  124. package/gsd-core/workflows/diagnose-issues.md +5 -1
  125. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  126. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  127. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  128. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  129. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  130. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  131. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  132. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  133. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  134. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  135. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  136. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  137. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  138. package/gsd-core/workflows/discuss-phase.md +1 -1
  139. package/gsd-core/workflows/do.md +43 -13
  140. package/gsd-core/workflows/docs-update.md +1 -1
  141. package/gsd-core/workflows/edit-phase.md +2 -0
  142. package/gsd-core/workflows/eval-review.md +1 -1
  143. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +2 -0
  144. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +17 -1
  145. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  146. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  147. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  148. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  149. package/gsd-core/workflows/execute-phase.md +32 -14
  150. package/gsd-core/workflows/execute-plan.md +8 -8
  151. package/gsd-core/workflows/explore.md +2 -0
  152. package/gsd-core/workflows/extract-learnings.md +2 -0
  153. package/gsd-core/workflows/fast.md +6 -0
  154. package/gsd-core/workflows/forensics.md +2 -0
  155. package/gsd-core/workflows/graduation.md +1 -1
  156. package/gsd-core/workflows/health.md +1 -1
  157. package/gsd-core/workflows/help/modes/brief.md +2 -0
  158. package/gsd-core/workflows/help/modes/default.md +2 -0
  159. package/gsd-core/workflows/help/modes/full.md +12 -0
  160. package/gsd-core/workflows/help/modes/topic.md +2 -0
  161. package/gsd-core/workflows/help.md +2 -0
  162. package/gsd-core/workflows/import.md +3 -3
  163. package/gsd-core/workflows/inbox.md +1 -1
  164. package/gsd-core/workflows/ingest-docs.md +1 -1
  165. package/gsd-core/workflows/insert-phase.md +2 -0
  166. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  167. package/gsd-core/workflows/list-seeds.md +2 -0
  168. package/gsd-core/workflows/list-workspaces.md +2 -0
  169. package/gsd-core/workflows/manager.md +3 -3
  170. package/gsd-core/workflows/map-codebase.md +2 -0
  171. package/gsd-core/workflows/milestone-summary.md +2 -0
  172. package/gsd-core/workflows/mvp-phase.md +1 -1
  173. package/gsd-core/workflows/new-milestone.md +1 -1
  174. package/gsd-core/workflows/new-project.md +5 -3
  175. package/gsd-core/workflows/new-workspace.md +1 -1
  176. package/gsd-core/workflows/next.md +2 -0
  177. package/gsd-core/workflows/node-repair.md +2 -0
  178. package/gsd-core/workflows/note.md +2 -0
  179. package/gsd-core/workflows/onboard.md +1 -1
  180. package/gsd-core/workflows/pause-work.md +19 -4
  181. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  182. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  183. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  184. package/gsd-core/workflows/plan-phase.md +130 -12
  185. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  186. package/gsd-core/workflows/plant-seed.md +1 -1
  187. package/gsd-core/workflows/pr-branch.md +11 -3
  188. package/gsd-core/workflows/profile-user.md +1 -1
  189. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  190. package/gsd-core/workflows/progress.md +25 -3
  191. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  192. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  193. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  194. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  195. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  196. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  197. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  198. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  199. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  200. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  201. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  202. package/gsd-core/workflows/quick-batch.md +203 -0
  203. package/gsd-core/workflows/quick.md +13 -3
  204. package/gsd-core/workflows/reapply-patches.md +2 -0
  205. package/gsd-core/workflows/remove-phase.md +2 -0
  206. package/gsd-core/workflows/remove-workspace.md +1 -1
  207. package/gsd-core/workflows/resume-project.md +6 -2
  208. package/gsd-core/workflows/review.md +215 -10
  209. package/gsd-core/workflows/scan.md +2 -0
  210. package/gsd-core/workflows/section-manifest.json +12 -0
  211. package/gsd-core/workflows/secure-phase.md +1 -1
  212. package/gsd-core/workflows/session-report.md +2 -0
  213. package/gsd-core/workflows/settings-advanced.md +2 -0
  214. package/gsd-core/workflows/settings-integrations.md +9 -8
  215. package/gsd-core/workflows/settings.md +1 -1
  216. package/gsd-core/workflows/ship.md +10 -10
  217. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  218. package/gsd-core/workflows/sketch.md +1 -1
  219. package/gsd-core/workflows/smart-entry.md +1 -1
  220. package/gsd-core/workflows/spec-phase.md +24 -19
  221. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  222. package/gsd-core/workflows/spike.md +1 -1
  223. package/gsd-core/workflows/stats.md +2 -0
  224. package/gsd-core/workflows/sync-skills.md +12 -4
  225. package/gsd-core/workflows/thread.md +2 -0
  226. package/gsd-core/workflows/transition.md +2 -0
  227. package/gsd-core/workflows/ui-phase.md +26 -5
  228. package/gsd-core/workflows/ui-review.md +1 -1
  229. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  230. package/gsd-core/workflows/undo.md +1 -1
  231. package/gsd-core/workflows/update.md +41 -38
  232. package/gsd-core/workflows/validate-phase.md +1 -1
  233. package/gsd-core/workflows/verify-work.md +49 -3
  234. package/hooks/dist/gsd-check-update-worker.js +19 -2
  235. package/hooks/dist/gsd-context-monitor.js +283 -12
  236. package/hooks/dist/gsd-node-runner.sh +1 -0
  237. package/hooks/dist/gsd-prompt-guard.js +30 -5
  238. package/hooks/dist/gsd-read-guard.js +2 -0
  239. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  240. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  241. package/hooks/dist/gsd-statusline.js +7 -3
  242. package/hooks/dist/gsd-validate-commit.sh +444 -7
  243. package/hooks/dist/gsd-workflow-guard.js +2 -1
  244. package/hooks/dist/lib/git-cmd.js +210 -1
  245. package/hooks/dist/lib/injection-patterns.js +36 -6
  246. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  247. package/hooks/gsd-check-update-worker.js +19 -2
  248. package/hooks/gsd-context-monitor.js +283 -12
  249. package/hooks/gsd-node-runner.sh +1 -0
  250. package/hooks/gsd-prompt-guard.js +30 -5
  251. package/hooks/gsd-read-guard.js +2 -0
  252. package/hooks/gsd-read-injection-scanner.js +5 -5
  253. package/hooks/gsd-secret-read-guard.js +1079 -0
  254. package/hooks/gsd-statusline.js +7 -3
  255. package/hooks/gsd-validate-commit.sh +444 -7
  256. package/hooks/gsd-workflow-guard.js +2 -1
  257. package/hooks/hooks.json +6 -0
  258. package/hooks/lib/git-cmd.js +210 -1
  259. package/hooks/lib/injection-patterns.js +36 -6
  260. package/hooks/managed-hooks-registry.cjs +1 -0
  261. package/package.json +5 -5
  262. package/scripts/build-hooks.js +11 -4
  263. package/scripts/ci-test-scope.cjs +7 -0
  264. package/scripts/docs-guard-registry.cjs +10 -0
  265. package/scripts/gen-loop-host-contract.cjs +67 -15
  266. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  267. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  268. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  269. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  270. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +5 -0
  271. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  272. package/scripts/lint-phase-id-drift.cjs +133 -8
  273. package/scripts/lint-portable-grep.cjs +176 -0
  274. package/scripts/lint-response-language-coverage.cjs +524 -0
  275. package/scripts/lint-test-file-count.allowlist.json +3 -1
  276. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  277. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  278. package/scripts/npm-audit-baseline.cjs +376 -0
  279. package/scripts/prompt-injection-scan.sh +8 -0
  280. package/scripts/require-issue-link-policy.cjs +16 -1
  281. package/skills/gsd-execute-phase/SKILL.md +1 -1
  282. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  283. package/skills/gsd-phase/SKILL.md +1 -1
  284. package/skills/gsd-quick-batch/SKILL.md +105 -0
  285. package/skills/gsd-surface/SKILL.md +18 -8
  286. package/vscode/package.json +1 -1
@@ -165,7 +165,7 @@ function normalizeSkillManifest(runtimeConfigDir, manifest) {
165
165
  * - registry is threaded into resolveProfile so capability skills
166
166
  * participate in the base skill set and their requires: chains expand.
167
167
  */
168
- function resolveSurface(runtimeConfigDir, manifest, clusterMap, registry) {
168
+ function resolveSurface(runtimeConfigDir, manifest, clusterMap, registry, surfaceOverride) {
169
169
  // Merge capability clusters into the cluster map when registry is provided.
170
170
  // The ADR-857 phase 4a HARD gate guarantees that when a capId matches a CLUSTERS
171
171
  // key, the values are EQUAL — so the spread is idempotent for matching names.
@@ -205,7 +205,7 @@ function resolveSurface(runtimeConfigDir, manifest, clusterMap, registry) {
205
205
  cm = merged;
206
206
  }
207
207
  const skillManifest = normalizeSkillManifest(runtimeConfigDir, manifest);
208
- const surface = readSurface(runtimeConfigDir);
208
+ const surface = surfaceOverride === undefined ? readSurface(runtimeConfigDir) : surfaceOverride;
209
209
  // Determine base profile name: from surface state or from .gsd-profile marker
210
210
  const baseProfileName = (surface && surface.baseProfile)
211
211
  ? surface.baseProfile
@@ -316,10 +316,9 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
316
316
  os: deps.os, env: deps.env,
317
317
  });
318
318
  const skillManifest = normalizeSkillManifest(layout.configDir, manifest);
319
- const resolved = resolveSurface(layout.configDir, skillManifest, clusterMap, registry);
320
- // Profile toggles must converge retired surfaces too. Once a kind disappears
321
- // from artifactLayout there is no normal sync pass left to prune it (#2644).
322
- retiredArtifactCleanup.pruneRetiredRuntimeArtifacts(layout.runtime, layout.configDir);
319
+ const hasCandidateState = opts !== undefined && Object.prototype.hasOwnProperty.call(opts, 'surfaceState');
320
+ const candidateState = hasCandidateState ? (opts.surfaceState ?? null) : undefined;
321
+ const resolved = resolveSurface(layout.configDir, skillManifest, clusterMap, registry, candidateState);
323
322
  // #1575: agents kind now mirrors createRuntimeArtifactInstallPlan — build
324
323
  // agentCtx (pathPrefix + attribution) and pass it to kind.stage() so
325
324
  // stageAgentsForRuntimeWithConverter applies the full inline-loop pipeline
@@ -353,7 +352,7 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
353
352
  // not referenced by any skill's _calls_agents_ manifest entry would be silently
354
353
  // dropped from the surface path. For tiered profiles (core/standard) or when
355
354
  // surface mods exist, pass the resolved set so only the filtered subset stages.
356
- const _surfaceState = readSurface(layout.configDir);
355
+ const _surfaceState = candidateState === undefined ? readSurface(layout.configDir) : candidateState;
357
356
  const _baseProfileName = (_surfaceState && _surfaceState.baseProfile)
358
357
  ? _surfaceState.baseProfile
359
358
  : (readActiveProfile(layout.configDir) || 'full');
@@ -361,6 +360,7 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
361
360
  _surfaceState.explicitAdds.length > 0 ||
362
361
  _surfaceState.explicitRemoves.length > 0);
363
362
  const _isUnmodifiedFull = _baseProfileName === 'full' && !_hasSurfaceMods;
363
+ const stagedKinds = [];
364
364
  try {
365
365
  for (const kind of layout.kinds) {
366
366
  let staged;
@@ -396,7 +396,22 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap, registry,
396
396
  // the parity test in tests/runtime-artifact-layout-surface.test.cjs
397
397
  // enforces that the two writers never diverge again.
398
398
  const dest = assertDestWithinConfigHome(kind.home ?? layout.configDir, kind.destSubpath);
399
- _syncGsdDir(staged, dest, kind, skillManifest, layout.runtime);
399
+ stagedKinds.push({ kind, staged, dest });
400
+ }
401
+ // Do not mutate installed artifacts until every kind has staged
402
+ // successfully. A missing source provider therefore leaves both artifacts
403
+ // and the persisted surface state untouched.
404
+ retiredArtifactCleanup.pruneRetiredRuntimeArtifacts(layout.runtime, layout.configDir);
405
+ for (const item of stagedKinds) {
406
+ _syncGsdDir(item.staged, item.dest, item.kind, skillManifest, layout.runtime);
407
+ }
408
+ if (hasCandidateState) {
409
+ if (candidateState === null) {
410
+ node_fs_1.default.rmSync(node_path_1.default.join(runtimeConfigDir, SURFACE_FILE_NAME), { force: true });
411
+ }
412
+ else if (candidateState !== undefined) {
413
+ writeSurface(runtimeConfigDir, candidateState);
414
+ }
400
415
  }
401
416
  }
402
417
  finally {
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ /**
3
+ * TDD RED-evidence classification (#3770).
4
+ *
5
+ * The `type: tdd` executor gate previously accepted ANY nonzero test command as
6
+ * RED: syntax errors, zero-test discovery, fixture crashes, parser errors, and
7
+ * unrelated assertions all authorized production edits (GREEN). This module
8
+ * defines the compact RED evidence the gate now requires — the TARGET test's
9
+ * identity plus a matching assertion failure — and classifies a persisted test
10
+ * run into exactly one verdict:
11
+ *
12
+ * RED_EVIDENCE_OK — nonzero exit AND the target test failed as a REAL test
13
+ * (distinctly named, TAP-reported failure). The ONLY
14
+ * verdict that may advance to GREEN.
15
+ * INVALID_RED — everything else, with a machine-readable reason:
16
+ * unexpected_green | zero_tests_discovered |
17
+ * nonzero_exit_without_test_failure |
18
+ * fixture_or_load_failure | no_target_test_failure |
19
+ * invalid_record | unreadable_record (router arm).
20
+ *
21
+ * Everything here is PURE — no fs, no spawn, no clock — so the executor can
22
+ * persist the record (command, exit code, failing test, expected, actual) and
23
+ * validate it via `gsd_run check tdd-red-evidence <record.json>`.
24
+ *
25
+ * TAP parsing reuses the proven primitives from prohibition-enforcement
26
+ * (`parseNodeTestSummary`, `tapFailedTestNames`) — the same contract the
27
+ * prohibition probe's fail-first prover already relies on (#1259).
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.classifyRedEvidence = classifyRedEvidence;
31
+ exports.buildRedEvidenceRecord = buildRedEvidenceRecord;
32
+ const prohibition_enforcement_cjs_1 = require("./prohibition-enforcement.cjs");
33
+ /** Basename of a path-like string ('' for non-strings) — separators `/` and `\`. */
34
+ function baseOf(p) {
35
+ return typeof p === 'string' ? (p.split(/[\\/]/).pop() ?? p) : '';
36
+ }
37
+ /** Coerce and validate the raw record's scalar fields. Returns null exit_code only when absent/non-numeric. */
38
+ function readInput(input) {
39
+ const command = typeof input?.command === 'string' ? input.command : '';
40
+ const output = typeof input?.output === 'string' ? input.output : '';
41
+ const targetTest = typeof input?.targetTest === 'string' ? input.targetTest.trim() : '';
42
+ const exitCode = typeof input?.exitCode === 'number' && Number.isFinite(input.exitCode) ? input.exitCode : null;
43
+ if (!command || !targetTest || exitCode === null)
44
+ return null;
45
+ return { command, exitCode, output, targetTest };
46
+ }
47
+ /**
48
+ * Classify a persisted RED-phase test run. Fail-closed: malformed input, an
49
+ * unparseable/empty TAP summary, a file-named (load/crash) failure, or a
50
+ * failure that is not the target test's are all INVALID_RED — only a nonzero
51
+ * exit WITH the distinctly-named target test failing is RED_EVIDENCE_OK.
52
+ * Never throws.
53
+ */
54
+ function classifyRedEvidence(input) {
55
+ const parsed = readInput(input);
56
+ if (!parsed) {
57
+ return {
58
+ verdict: 'INVALID_RED',
59
+ reason: 'invalid_record',
60
+ evidence: {
61
+ command: typeof input?.command === 'string' ? input.command : '',
62
+ exit_code: null,
63
+ target_test: '',
64
+ tests: 0,
65
+ pass: 0,
66
+ fail: 0,
67
+ failing_tests: [],
68
+ },
69
+ };
70
+ }
71
+ const { command, exitCode, output, targetTest } = parsed;
72
+ const summary = (0, prohibition_enforcement_cjs_1.parseNodeTestSummary)(output);
73
+ const failing = (0, prohibition_enforcement_cjs_1.tapFailedTestNames)(output);
74
+ const evidence = {
75
+ command,
76
+ exit_code: exitCode,
77
+ target_test: targetTest,
78
+ tests: summary.tests,
79
+ pass: summary.pass,
80
+ fail: summary.fail,
81
+ failing_tests: failing,
82
+ };
83
+ // Existing fail-fast rule, now machine-checked: exit 0 during RED is an
84
+ // unexpected GREEN — the feature may already exist or the test is wrong.
85
+ if (exitCode === 0) {
86
+ return { verdict: 'INVALID_RED', reason: 'unexpected_green', evidence };
87
+ }
88
+ // Zero-test discovery: the discovery pattern / fixture matched no tests.
89
+ // A run that executed nothing cannot prove anything about the behavior.
90
+ if (summary.tests === 0) {
91
+ return { verdict: 'INVALID_RED', reason: 'zero_tests_discovered', evidence };
92
+ }
93
+ // Nonzero exit but TAP reports no failing test: harness/setup/parser crash
94
+ // whose failure never reached a test assertion (or unparseable output).
95
+ if (summary.fail === 0 || failing.length === 0) {
96
+ return { verdict: 'INVALID_RED', reason: 'nonzero_exit_without_test_failure', evidence };
97
+ }
98
+ // Fixture/load failure: every failing entry is named like the target FILE —
99
+ // node reports a load-time crash (throw-on-require, syntax error, ENOENT
100
+ // fixture) as a file-named `not ok 1 - <file>`, never the target test.
101
+ const targetBase = baseOf(input?.targetFile ?? '');
102
+ const distinctlyNamed = failing.filter((n) => (targetBase ? baseOf(n) !== targetBase : true));
103
+ if (distinctlyNamed.length === 0) {
104
+ return { verdict: 'INVALID_RED', reason: 'fixture_or_load_failure', evidence };
105
+ }
106
+ // Unrelated failure: real tests ran and failed, but none is the target test
107
+ // the plan named — an unrelated assertion must not authorize GREEN.
108
+ if (!distinctlyNamed.includes(targetTest)) {
109
+ return { verdict: 'INVALID_RED', reason: 'no_target_test_failure', evidence };
110
+ }
111
+ return { verdict: 'RED_EVIDENCE_OK', reason: 'target_test_failed', evidence };
112
+ }
113
+ /**
114
+ * Project a classification into the persisted record shape — command, exit
115
+ * code, failing test, expected, actual, verdict, reason — so the evidence
116
+ * survives past the terminal and the gate can re-verify it deterministically.
117
+ * Pure: JSON-serializable, no timestamps (the record's mtime/commit carries time).
118
+ */
119
+ function buildRedEvidenceRecord(input, result) {
120
+ const failingTest = result.evidence.failing_tests.find((n) => n === result.evidence.target_test) ??
121
+ result.evidence.failing_tests[0] ??
122
+ null;
123
+ return {
124
+ command: result.evidence.command,
125
+ exit_code: result.evidence.exit_code,
126
+ failing_test: failingTest,
127
+ target_test: result.evidence.target_test,
128
+ expected: typeof input?.expected === 'string' ? input.expected : null,
129
+ actual: typeof input?.actual === 'string' ? input.actual : null,
130
+ verdict: result.verdict,
131
+ reason: result.reason,
132
+ };
133
+ }