@opengsd/gsd-core 1.13.0 → 1.14.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 (257) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-advisor-researcher.compact.md +85 -0
  4. package/agents/gsd-ai-researcher.compact.md +96 -0
  5. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  6. package/agents/gsd-code-fixer.compact.md +458 -0
  7. package/agents/gsd-code-fixer.md +5 -5
  8. package/agents/gsd-code-reviewer.compact.md +269 -0
  9. package/agents/gsd-code-reviewer.md +15 -3
  10. package/agents/gsd-codebase-mapper.compact.md +760 -0
  11. package/agents/gsd-debug-session-manager.compact.md +345 -0
  12. package/agents/gsd-doc-classifier.compact.md +192 -0
  13. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  14. package/agents/gsd-doc-verifier.compact.md +143 -0
  15. package/agents/gsd-doc-writer.compact.md +440 -0
  16. package/agents/gsd-dom-verifier.compact.md +138 -0
  17. package/agents/gsd-domain-researcher.compact.md +141 -0
  18. package/agents/gsd-eval-auditor.compact.md +160 -0
  19. package/agents/gsd-eval-planner.compact.md +137 -0
  20. package/agents/gsd-framework-selector.compact.md +82 -0
  21. package/agents/gsd-integration-checker.compact.md +245 -0
  22. package/agents/gsd-intel-updater.compact.md +226 -0
  23. package/agents/gsd-mempalace-curator.compact.md +45 -0
  24. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  25. package/agents/gsd-pattern-mapper.compact.md +275 -0
  26. package/agents/gsd-project-researcher.compact.md +587 -0
  27. package/agents/gsd-research-synthesizer.compact.md +212 -0
  28. package/agents/gsd-roadmapper.compact.md +454 -0
  29. package/agents/gsd-roadmapper.md +13 -0
  30. package/agents/gsd-security-auditor.compact.md +162 -0
  31. package/agents/gsd-ui-auditor.compact.md +404 -0
  32. package/agents/gsd-ui-checker.compact.md +277 -0
  33. package/agents/gsd-ui-researcher.compact.md +282 -0
  34. package/agents/gsd-user-profiler.compact.md +108 -0
  35. package/bin/install.js +206 -68
  36. package/commands/gsd/cleanup.md +1 -0
  37. package/commands/gsd/code-review.md +2 -1
  38. package/commands/gsd/complete-milestone.md +1 -0
  39. package/commands/gsd/config.md +1 -0
  40. package/commands/gsd/debug.md +1 -0
  41. package/commands/gsd/graphify.md +1 -0
  42. package/commands/gsd/health.md +1 -0
  43. package/commands/gsd/mempalace-capture.md +1 -0
  44. package/commands/gsd/mempalace-recall.md +1 -0
  45. package/commands/gsd/new-milestone.md +1 -0
  46. package/commands/gsd/new-project.md +1 -0
  47. package/commands/gsd/next.md +1 -0
  48. package/commands/gsd/pause-work.md +1 -0
  49. package/commands/gsd/phase.md +1 -0
  50. package/commands/gsd/pr-branch.md +1 -0
  51. package/commands/gsd/resume-work.md +1 -0
  52. package/commands/gsd/review-backlog.md +1 -0
  53. package/commands/gsd/settings.md +2 -1
  54. package/commands/gsd/stats.md +1 -0
  55. package/commands/gsd/thread.md +1 -0
  56. package/commands/gsd/workspace.md +1 -0
  57. package/commands/gsd/workstreams.md +1 -0
  58. package/gsd-core/bin/check-latest-version.cjs +8 -3
  59. package/gsd-core/bin/gsd-tools.cjs +338 -125
  60. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  61. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  62. package/gsd-core/bin/lib/audit.cjs +39 -22
  63. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  64. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  65. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  66. package/gsd-core/bin/lib/capability-registry.cjs +79 -67
  67. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  68. package/gsd-core/bin/lib/capability-validator.cjs +14 -1
  69. package/gsd-core/bin/lib/check-command-router.cjs +113 -36
  70. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  71. package/gsd-core/bin/lib/commands.cjs +650 -72
  72. package/gsd-core/bin/lib/config-loader.cjs +1 -0
  73. package/gsd-core/bin/lib/config.cjs +153 -38
  74. package/gsd-core/bin/lib/coverage.cjs +1 -1
  75. package/gsd-core/bin/lib/decisions.cjs +137 -34
  76. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  77. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  78. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
  79. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  80. package/gsd-core/bin/lib/init.cjs +409 -47
  81. package/gsd-core/bin/lib/install-engine.cjs +16 -3
  82. package/gsd-core/bin/lib/install-profiles.cjs +14 -0
  83. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  84. package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
  85. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  86. package/gsd-core/bin/lib/milestone.cjs +19 -8
  87. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  88. package/gsd-core/bin/lib/phase-command-router.cjs +7 -1
  89. package/gsd-core/bin/lib/phase-id.cjs +161 -22
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  91. package/gsd-core/bin/lib/phase.cjs +167 -63
  92. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  93. package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
  94. package/gsd-core/bin/lib/planning-workspace.cjs +50 -1
  95. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  96. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  97. package/gsd-core/bin/lib/quick-batch.cjs +1 -1
  98. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  99. package/gsd-core/bin/lib/research-store.cjs +11 -12
  100. package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
  101. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  102. package/gsd-core/bin/lib/roadmap-parser.cjs +56 -15
  103. package/gsd-core/bin/lib/roadmap.cjs +108 -14
  104. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +27 -10
  105. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +12 -3
  106. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
  107. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +193 -4
  108. package/gsd-core/bin/lib/security.cjs +126 -7
  109. package/gsd-core/bin/lib/state-document.cjs +130 -28
  110. package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
  111. package/gsd-core/bin/lib/state-transition.cjs +142 -28
  112. package/gsd-core/bin/lib/state.cjs +223 -27
  113. package/gsd-core/bin/lib/surface.cjs +60 -2
  114. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  115. package/gsd-core/bin/lib/uat.cjs +1 -1
  116. package/gsd-core/bin/lib/update-context.cjs +30 -24
  117. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  118. package/gsd-core/bin/lib/verification.cjs +47 -15
  119. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  120. package/gsd-core/bin/lib/verify.cjs +188 -23
  121. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
  122. package/gsd-core/bin/lib/worktree-safety.cjs +13 -7
  123. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  124. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  125. package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
  126. package/gsd-core/references/compact-content-gate.md +66 -0
  127. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  128. package/gsd-core/references/model-profiles.md +12 -3
  129. package/gsd-core/references/planning-config.md +3 -0
  130. package/gsd-core/references/tdd.md +5 -2
  131. package/gsd-core/references/thinking-models-planning.md +18 -2
  132. package/gsd-core/references/verification-patterns.md +17 -4
  133. package/gsd-core/references/worktree-path-safety.md +112 -2
  134. package/gsd-core/templates/README.md +7 -1
  135. package/gsd-core/templates/state.md +6 -3
  136. package/gsd-core/templates/summary.compact.md +212 -0
  137. package/gsd-core/templates/user-setup.compact.md +199 -0
  138. package/gsd-core/templates/user-setup.md +0 -9
  139. package/gsd-core/workflows/add-todo.md +3 -2
  140. package/gsd-core/workflows/autonomous.md +13 -10
  141. package/gsd-core/workflows/check-todos.md +4 -2
  142. package/gsd-core/workflows/cleanup.md +3 -1
  143. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +7 -0
  144. package/gsd-core/workflows/code-review-fix.md +3 -3
  145. package/gsd-core/workflows/code-review.md +156 -30
  146. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  147. package/gsd-core/workflows/complete-milestone.md +39 -262
  148. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  149. package/gsd-core/workflows/docs-update.md +14 -155
  150. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  151. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +18 -3
  152. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  153. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +7 -2
  154. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  155. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  156. package/gsd-core/workflows/execute-phase.md +53 -152
  157. package/gsd-core/workflows/execute-plan.md +20 -7
  158. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  159. package/gsd-core/workflows/help.md +1 -1
  160. package/gsd-core/workflows/map-codebase.md +50 -3
  161. package/gsd-core/workflows/new-milestone.md +54 -12
  162. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  163. package/gsd-core/workflows/new-project.md +32 -202
  164. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  165. package/gsd-core/workflows/plan-phase.md +22 -181
  166. package/gsd-core/workflows/pr-branch.md +19 -7
  167. package/gsd-core/workflows/quick.md +8 -1
  168. package/gsd-core/workflows/reapply-patches.md +77 -3
  169. package/gsd-core/workflows/settings.md +18 -5
  170. package/gsd-core/workflows/update.md +7 -5
  171. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  172. package/gsd-core/workflows/verify-work.md +20 -180
  173. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  174. package/hooks/dist/gsd-context-monitor.js +88 -15
  175. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  176. package/hooks/dist/gsd-secret-read-guard.js +44 -18
  177. package/hooks/dist/gsd-statusline.js +11 -7
  178. package/hooks/dist/gsd-validate-commit.sh +34 -4
  179. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  180. package/hooks/dist/gsd-write-guard.js +46 -1
  181. package/hooks/dist/lib/dispatch-identity.js +187 -0
  182. package/hooks/dist/lib/filename-classification.js +64 -0
  183. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  184. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  185. package/hooks/gsd-agent-isolation-guard.js +42 -16
  186. package/hooks/gsd-context-monitor.js +88 -15
  187. package/hooks/gsd-cursor-subagent-start.js +34 -14
  188. package/hooks/gsd-secret-read-guard.js +44 -18
  189. package/hooks/gsd-statusline.js +11 -7
  190. package/hooks/gsd-validate-commit.sh +34 -4
  191. package/hooks/gsd-worktree-path-guard.js +25 -14
  192. package/hooks/gsd-write-guard.js +46 -1
  193. package/hooks/lib/dispatch-identity.js +187 -0
  194. package/hooks/lib/filename-classification.js +64 -0
  195. package/hooks/lib/isolation-deny-reason.js +53 -1
  196. package/hooks/lib/isolation-sentinel.js +58 -19
  197. package/package.json +10 -6
  198. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  199. package/scripts/benchmark-compact-content.cjs +368 -0
  200. package/scripts/check-contract-drift.cjs +4 -1
  201. package/scripts/check-env.cjs +36 -8
  202. package/scripts/check-glossary-refs.cjs +25 -21
  203. package/scripts/ci-next-health.cjs +271 -0
  204. package/scripts/ci-prepare-test-scope.cjs +7 -7
  205. package/scripts/ci-test-scope.cjs +126 -20
  206. package/scripts/ci-timeout-report.cjs +1 -1
  207. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  208. package/scripts/docs-guard-registry.cjs +7 -2
  209. package/scripts/gen-adr-index.cjs +8 -2
  210. package/scripts/gen-inventory-manifest.cjs +12 -0
  211. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  212. package/scripts/lib/drift-scan.cjs +1 -1
  213. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  214. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  215. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  216. package/scripts/lib/suite-detection.cjs +32 -0
  217. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  218. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +19 -2
  219. package/scripts/lint-phase-id-drift.cjs +338 -13
  220. package/scripts/lint-response-language-coverage.cjs +9 -3
  221. package/scripts/lint-source-test-name-collision.cjs +1 -1
  222. package/scripts/lint-test-file-count.allowlist.json +1 -0
  223. package/scripts/lint-vendored-deps.cjs +128 -17
  224. package/scripts/lint-workflow-shellcheck-baseline.json +85 -0
  225. package/scripts/prompt-injection-scan.sh +14 -0
  226. package/scripts/workflow-size.cjs +139 -0
  227. package/skills/gsd-cleanup/SKILL.md +1 -0
  228. package/skills/gsd-code-review/SKILL.md +2 -1
  229. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  230. package/skills/gsd-config/SKILL.md +1 -0
  231. package/skills/gsd-debug/SKILL.md +1 -0
  232. package/skills/gsd-graphify/SKILL.md +1 -0
  233. package/skills/gsd-health/SKILL.md +1 -0
  234. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  235. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  236. package/skills/gsd-new-milestone/SKILL.md +1 -0
  237. package/skills/gsd-new-project/SKILL.md +1 -0
  238. package/skills/gsd-next/SKILL.md +1 -0
  239. package/skills/gsd-pause-work/SKILL.md +1 -0
  240. package/skills/gsd-phase/SKILL.md +1 -0
  241. package/skills/gsd-pr-branch/SKILL.md +1 -0
  242. package/skills/gsd-resume-work/SKILL.md +1 -0
  243. package/skills/gsd-review-backlog/SKILL.md +1 -0
  244. package/skills/gsd-settings/SKILL.md +2 -1
  245. package/skills/gsd-stats/SKILL.md +1 -0
  246. package/skills/gsd-thread/SKILL.md +1 -0
  247. package/skills/gsd-workspace/SKILL.md +1 -0
  248. package/skills/gsd-workstreams/SKILL.md +1 -0
  249. package/vscode/package.json +1 -1
  250. package/gsd-core/templates/claude-md.md +0 -145
  251. package/gsd-core/templates/codebase/concerns.md +0 -310
  252. package/gsd-core/templates/codebase/conventions.md +0 -307
  253. package/gsd-core/templates/codebase/integrations.md +0 -280
  254. package/gsd-core/templates/codebase/structure.md +0 -285
  255. package/gsd-core/templates/codebase/testing.md +0 -480
  256. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  257. package/gsd-core/templates/discovery.md +0 -146
@@ -0,0 +1,274 @@
1
+ # complete-milestone.md — deferred elaboration
2
+
3
+ Read in full when `workflow.compact_content` is `false` (the default) — see
4
+ `gsd-core/references/compact-content-gate.md` for the check and resolution rule this
5
+ spine defers to. Each `§` below is the full text the spine condenses at the point it
6
+ names.
7
+
8
+ ## § 1 — pre_close_artifact_audit: the [A] Acknowledge branch
9
+
10
+ If user chooses [A] (Acknowledge):
11
+ 1. Re-run `gsd_run query audit-open --json` to get structured data.
12
+ 2. Acknowledge every open item through the `audit-open acknowledge` CLI writer — this is what actually suppresses each item starting at the NEXT `audit-open` scan; the STATE.md table in step 3 is a disclosure record only, it is no longer the suppression mechanism. Every acknowledge call's exit status is accumulated (`ACK_FAILURES`); the step HALTS before closing if any failed — a refusal (`unsupported_heading_shape`, `ambiguous`, `not_found`, missing file, etc.) must never be silently discarded and let the close proceed as if everything were suppressed. `AUDIT_JSON` uses the same `@file:` large-payload sentinel handling `INIT_MANAGER` uses in `verify_readiness` below — `io.output` swaps any JSON payload over 50000 chars for a `@file:<path>` marker, and feeding that literal string to `jq` would silently make every loop body below iterate zero times:
13
+ ```bash
14
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif _gsd_at "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd_run is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; case "$(gsd_run runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') GSD_IDENTITY_STATUS=ok;; esac; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
15
+ AUDIT_JSON=$(gsd_run query audit-open --json)
16
+ if [[ "$AUDIT_JSON" == @file:* ]]; then AUDIT_JSON=$(cat "${AUDIT_JSON#@file:}"); fi
17
+ MILESTONE_VERSION="v[X.Y]" # already known from ROADMAP.md's active milestone header — the same identifier `milestone.complete` uses in the archive_milestone step
18
+
19
+ ACK_FAILURES=0
20
+ ACK_FAILURE_LOG=""
21
+ record_ack_failure() {
22
+ ACK_FAILURES=$((ACK_FAILURES + 1))
23
+ ACK_FAILURE_LOG="${ACK_FAILURE_LOG}
24
+ - $1"
25
+ }
26
+
27
+ # debug_sessions / threads (--slug)
28
+ # NOTE: `< <(...)` process substitution, not `... | while`, so the loop
29
+ # runs in THIS shell — a `| while` pipeline puts the loop in a subshell
30
+ # and any ACK_FAILURES/ACK_FAILURE_LOG update inside it is lost the
31
+ # moment the pipeline exits.
32
+ for cat in debug_sessions threads; do
33
+ while IFS= read -r slug; do
34
+ [ -z "$slug" ] && continue
35
+ if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --slug "$slug"; then
36
+ record_ack_failure "$cat slug=$slug"
37
+ fi
38
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r --arg cat "$cat" '.items[$cat][] | select(.scan_error | not) | .slug')
39
+ done
40
+
41
+ # seeds (--seed-id)
42
+ while IFS= read -r seed_id; do
43
+ [ -z "$seed_id" ] && continue
44
+ if ! gsd_run query audit-open acknowledge --category seeds --milestone "$MILESTONE_VERSION" --seed-id "$seed_id"; then
45
+ record_ack_failure "seeds seed_id=$seed_id"
46
+ fi
47
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.seeds[] | select(.scan_error | not) | .seed_id')
48
+
49
+ # todos (--filename) — the scanner caps its list to 5 entries per scan
50
+ # (remainder items carry `_remainder_count`, no `filename`, and are skipped)
51
+ while IFS= read -r filename; do
52
+ [ -z "$filename" ] && continue
53
+ if ! gsd_run query audit-open acknowledge --category todos --milestone "$MILESTONE_VERSION" --filename "$filename"; then
54
+ record_ack_failure "todos filename=$filename"
55
+ fi
56
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.todos[] | select((.scan_error or ._remainder_count) | not) | .filename')
57
+
58
+ # quick_tasks (--dir) — the scanner's `slug` strips a leading
59
+ # YYYYMMDD-/YYYY-MM-DD- date prefix for display; `--dir` needs the
60
+ # ORIGINAL .planning/quick/<dir>/ name, so reconstruct it from `date`+`slug`.
61
+ while IFS= read -r dir; do
62
+ [ -z "$dir" ] && continue
63
+ if ! gsd_run query audit-open acknowledge --category quick_tasks --milestone "$MILESTONE_VERSION" --dir "$dir"; then
64
+ record_ack_failure "quick_tasks dir=$dir"
65
+ fi
66
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.quick_tasks[] | select(.scan_error | not) | if .date != "" then "\(.date)-\(.slug)" else .slug end')
67
+
68
+ # uat_gaps / verification_gaps / context_questions — phase-scoped
69
+ # (--phase --file [--archived-milestone] when the item was found in an archived phase)
70
+ for cat in uat_gaps verification_gaps context_questions; do
71
+ while IFS= read -r item; do
72
+ [ -z "$item" ] && continue
73
+ phase=$(printf '%s' "$item" | jq -r '.phase')
74
+ file=$(printf '%s' "$item" | jq -r '.file')
75
+ archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty')
76
+ if [ -n "$archived" ]; then
77
+ if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --archived-milestone "$archived"; then
78
+ record_ack_failure "$cat phase=$phase file=$file archived-milestone=$archived"
79
+ fi
80
+ else
81
+ if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file"; then
82
+ record_ack_failure "$cat phase=$phase file=$file"
83
+ fi
84
+ fi
85
+ done < <(printf '%s' "$AUDIT_JSON" | jq -c --arg cat "$cat" '.items[$cat][] | select(.scan_error | not)')
86
+ done
87
+
88
+ # deferred_items — same phase-scoped identification, plus --text (the
89
+ # exact bullet the audit read, which uniquely identifies the entry)
90
+ while IFS= read -r item; do
91
+ [ -z "$item" ] && continue
92
+ phase=$(printf '%s' "$item" | jq -r '.phase')
93
+ file=$(printf '%s' "$item" | jq -r '.file')
94
+ text=$(printf '%s' "$item" | jq -r '.text')
95
+ archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty')
96
+ if [ -n "$archived" ]; then
97
+ if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text" --archived-milestone "$archived"; then
98
+ record_ack_failure "deferred_items phase=$phase file=$file archived-milestone=$archived"
99
+ fi
100
+ else
101
+ if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text"; then
102
+ record_ack_failure "deferred_items phase=$phase file=$file"
103
+ fi
104
+ fi
105
+ done < <(printf '%s' "$AUDIT_JSON" | jq -c '.items.deferred_items[] | select(.scan_error | not)')
106
+
107
+ if [ "$ACK_FAILURES" -gt 0 ]; then
108
+ echo "ERROR: $ACK_FAILURES acknowledge call(s) failed — HALTING before milestone close. Resolve each listed item manually (e.g. edit the file directly for unsupported_heading_shape/ambiguous, or re-run the audit if a --text/--file target has since changed) and re-run /gsd:complete-milestone:" >&2
109
+ printf '%s\n' "$ACK_FAILURE_LOG" >&2
110
+ exit 1
111
+ fi
112
+ ```
113
+ `todos` is the only category the scanner caps (5 entries per scan, with a remainder count for the rest). Re-run `gsd_run query audit-open --json` (through the same `@file:` handling above) and repeat the `todos` block until it reports no `todos` items — every other category always returns its full open set in one pass.
114
+ 3. Re-run `gsd_run query audit-open --json` once more and write the items just acknowledged as new rows to STATE.md under `## Deferred Items` — append to the existing table (creating the section if absent) rather than overwriting it, preserving rows recorded at earlier milestone closes:
115
+ ```markdown
116
+ ## Deferred Items
117
+
118
+ Items acknowledged and deferred at milestone close, most recent first:
119
+
120
+ | Category | Item | Status | Deferred At | Milestone |
121
+ |----------|------|--------|-------------|-----------|
122
+ | debug_sessions | {slug} | {status} | {date} | {milestone} |
123
+ | quick_tasks | {slug} | {status} | {date} | {milestone} |
124
+ | threads | {slug} | {status} | {date} | {milestone} |
125
+ | seeds | {seed_id} | {status} | {date} | {milestone} |
126
+ | todos | {filename} | (presence-only) | {date} | {milestone} |
127
+ | uat_gaps | {phase}/{file} | {status} | {date} | {milestone} |
128
+ | verification_gaps | {phase}/{file} | {status} | {date} | {milestone} |
129
+ | context_questions | {phase}/{file} | {question_count} questions | {date} | {milestone} |
130
+ | deferred_items | {phase}/{file}: {text} | acknowledged | {date} | {milestone} |
131
+ ```
132
+ One row per item actually acknowledged in step 2 (omit categories with nothing to disclose this close). `{date}` is today's date; `{milestone}` is `MILESTONE_VERSION`. Sanitize all slug/status/text values via `sanitizeForDisplay()` before writing. Never inject raw file content into STATE.md.
133
+ 4. Set `closeout_type=override_closeout` and record in the MILESTONES.md entry: `Known verification overrides: {N} newly acknowledged, {M} carried forward from a prior close (see STATE.md Deferred Items)` — `{N}` is the count of items acknowledged in step 2 (the pre-acknowledgment audit JSON's `counts.total`) and `{M}` is that same audit JSON's `acknowledged.total` (items a PRIOR close already suppressed and still are).
134
+ 5. Proceed with milestone close.
135
+
136
+ Acknowledging is verdict-preserving and self-invalidating: it never rewrites the artifact's own `status:` field (except `deferred_items`, whose entry has no other meaning for that field), and the suppression it grants lapses automatically the moment the artifact's observed state changes again — a reopened debug session, an edited UAT gap, a re-triggered seed, etc. resurfaces on its own at the next audit and must be acknowledged again.
137
+
138
+ If output shows all clear (no open items): set `closeout_type=verified_closeout`. If the audit JSON's `acknowledged.total` is `0`, print `All artifact types clear.` and proceed. Otherwise the close is clean only because `{acknowledged.total}` item(s) acknowledged at an earlier milestone close are still being suppressed, not because everything was fixed this time — print `All artifact types clear ({acknowledged.total} previously acknowledged item(s) still suppressed — see STATE.md Deferred Items).` and record `Known verification overrides: 0 newly acknowledged, {acknowledged.total} carried forward from a prior close (see STATE.md Deferred Items)` in the MILESTONES.md entry before proceeding.
139
+
140
+ (The SECURITY note on audit JSON provenance and STATE.md sanitization is stated in the spine, not repeated here.)
141
+
142
+ ## § 2 — handle_branches
143
+
144
+ Check branching strategy and offer merge options.
145
+
146
+ Use `init milestone-op` for context, or load config directly:
147
+
148
+ ```bash
149
+ INIT=$(gsd_run query init.execute-phase "1")
150
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
151
+ INIT_CM=$(gsd_run query init.complete-milestone)
152
+ if [[ "$INIT_CM" == @file:* ]]; then INIT_CM=$(cat "${INIT_CM#@file:}"); fi
153
+ ```
154
+
155
+ Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_template`, and `commit_docs` from init JSON. Extract `git_create_tag` and `section_manifest` from `INIT_CM` (used by the `git_tag` step below).
156
+
157
+ `BASE_BRANCH` is already resolved by the spine at this point (via the shared `git.base-branch` resolver) — the branch options below use it as-is.
158
+
159
+ **If "none":** Skip to git_tag.
160
+
161
+ **For "phase" strategy:**
162
+
163
+ ```bash
164
+ BRANCH_PREFIX=$(echo "$PHASE_BRANCH_TEMPLATE" | sed 's/{.*//')
165
+ PHASE_BRANCHES=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ')
166
+ ```
167
+
168
+ **For "milestone" strategy:**
169
+
170
+ ```bash
171
+ BRANCH_PREFIX=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed 's/{.*//')
172
+ MILESTONE_BRANCH=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ' | head -1)
173
+ ```
174
+
175
+ **If no branches found:** Skip to git_tag.
176
+
177
+ **If branches exist:**
178
+
179
+ ```
180
+ ## Git Branches Detected
181
+
182
+ Branching strategy: {phase/milestone}
183
+ Branches: {list}
184
+
185
+ Options:
186
+ 1. **Merge to main** — Merge branch(es) to main
187
+ 2. **Delete without merging** — Already merged or not needed
188
+ 3. **Keep branches** — Leave for manual handling
189
+ ```
190
+
191
+ AskUserQuestion with options: Squash merge (Recommended), Merge with history, Delete without merging, Keep branches.
192
+
193
+ **Squash merge:**
194
+
195
+ ```bash
196
+ CURRENT_BRANCH=$(git branch --show-current)
197
+ git checkout ${BASE_BRANCH}
198
+
199
+ if [ "$BRANCHING_STRATEGY" = "phase" ]; then
200
+ # Rewrapped through unquoted command substitution (gsd-core#4109): a bare
201
+ # `$VAR` word-splits under bash but not zsh, collapsing every element onto
202
+ # one iteration there.
203
+ for branch in $(printf '%s' "$PHASE_BRANCHES"); do
204
+ git merge --squash "$branch"
205
+ # Strip .planning/ from staging if commit_docs is false
206
+ if [ "$COMMIT_DOCS" = "false" ]; then
207
+ git reset HEAD .planning/ 2>/dev/null || true
208
+ fi
209
+ git commit -m "feat: $branch for v[X.Y]"
210
+ done
211
+ fi
212
+
213
+ if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
214
+ git merge --squash "$MILESTONE_BRANCH"
215
+ # Strip .planning/ from staging if commit_docs is false
216
+ if [ "$COMMIT_DOCS" = "false" ]; then
217
+ git reset HEAD .planning/ 2>/dev/null || true
218
+ fi
219
+ git commit -m "feat: $MILESTONE_BRANCH for v[X.Y]"
220
+ fi
221
+
222
+ git checkout "$CURRENT_BRANCH"
223
+ ```
224
+
225
+ **Merge with history:**
226
+
227
+ ```bash
228
+ CURRENT_BRANCH=$(git branch --show-current)
229
+ git checkout ${BASE_BRANCH}
230
+
231
+ if [ "$BRANCHING_STRATEGY" = "phase" ]; then
232
+ # Rewrapped through unquoted command substitution (gsd-core#4109): a bare
233
+ # `$VAR` word-splits under bash but not zsh, collapsing every element onto
234
+ # one iteration there.
235
+ for branch in $(printf '%s' "$PHASE_BRANCHES"); do
236
+ git merge --no-ff --no-commit "$branch"
237
+ # Strip .planning/ from staging if commit_docs is false
238
+ if [ "$COMMIT_DOCS" = "false" ]; then
239
+ git reset HEAD .planning/ 2>/dev/null || true
240
+ fi
241
+ git commit -m "Merge branch '$branch' for v[X.Y]"
242
+ done
243
+ fi
244
+
245
+ if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
246
+ git merge --no-ff --no-commit "$MILESTONE_BRANCH"
247
+ # Strip .planning/ from staging if commit_docs is false
248
+ if [ "$COMMIT_DOCS" = "false" ]; then
249
+ git reset HEAD .planning/ 2>/dev/null || true
250
+ fi
251
+ git commit -m "Merge branch '$MILESTONE_BRANCH' for v[X.Y]"
252
+ fi
253
+
254
+ git checkout "$CURRENT_BRANCH"
255
+ ```
256
+
257
+ **Delete without merging:**
258
+
259
+ ```bash
260
+ if [ "$BRANCHING_STRATEGY" = "phase" ]; then
261
+ # Rewrapped through unquoted command substitution (gsd-core#4109): a bare
262
+ # `$VAR` word-splits under bash but not zsh, collapsing every element onto
263
+ # one iteration there.
264
+ for branch in $(printf '%s' "$PHASE_BRANCHES"); do
265
+ git branch -d "$branch" 2>/dev/null || git branch -D "$branch"
266
+ done
267
+ fi
268
+
269
+ if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
270
+ git branch -d "$MILESTONE_BRANCH" 2>/dev/null || git branch -D "$MILESTONE_BRANCH"
271
+ fi
272
+ ```
273
+
274
+ **Keep branches:** Report "Branches preserved for manual handling"
@@ -37,6 +37,8 @@ When a milestone completes:
37
37
 
38
38
  <process>
39
39
 
40
+ **Compact Content Gate.** Read and follow `gsd-core/references/compact-content-gate.md` now — it states the `workflow.compact_content` check and the resolution rule this spine defers to. When it directs a Read, read `gsd-core/workflows/complete-milestone/detail/elaboration.md` in full before continuing past this point; its content elaborates on the audit-acknowledge branch and the handle_branches step below.
41
+
40
42
  <step name="pre_close_artifact_audit">
41
43
  Before proceeding with milestone close, run the comprehensive open artifact audit.
42
44
 
@@ -60,136 +62,14 @@ These items are open. Choose an action:
60
62
  [C] Cancel — exit without closing
61
63
  ```
62
64
 
63
- If user chooses [A] (Acknowledge):
64
- 1. Re-run `gsd_run query audit-open --json` to get structured data.
65
- 2. Acknowledge every open item through the `audit-open acknowledge` CLI writer — this is what actually suppresses each item starting at the NEXT `audit-open` scan; the STATE.md table in step 3 is a disclosure record only, it is no longer the suppression mechanism. Every acknowledge call's exit status is accumulated (`ACK_FAILURES`); the step HALTS before closing if any failed — a refusal (`unsupported_heading_shape`, `ambiguous`, `not_found`, missing file, etc.) must never be silently discarded and let the close proceed as if everything were suppressed. `AUDIT_JSON` uses the same `@file:` large-payload sentinel handling `INIT_MANAGER` uses in `verify_readiness` below — `io.output` swaps any JSON payload over 50000 chars for a `@file:<path>` marker, and feeding that literal string to `jq` would silently make every loop body below iterate zero times:
66
- ```bash
67
- AUDIT_JSON=$(gsd_run query audit-open --json)
68
- if [[ "$AUDIT_JSON" == @file:* ]]; then AUDIT_JSON=$(cat "${AUDIT_JSON#@file:}"); fi
69
- MILESTONE_VERSION="v[X.Y]" # already known from ROADMAP.md's active milestone header — the same identifier `milestone.complete` uses in the archive_milestone step
70
-
71
- ACK_FAILURES=0
72
- ACK_FAILURE_LOG=""
73
- record_ack_failure() {
74
- ACK_FAILURES=$((ACK_FAILURES + 1))
75
- ACK_FAILURE_LOG="${ACK_FAILURE_LOG}
76
- - $1"
77
- }
78
-
79
- # debug_sessions / threads (--slug)
80
- # NOTE: `< <(...)` process substitution, not `... | while`, so the loop
81
- # runs in THIS shell — a `| while` pipeline puts the loop in a subshell
82
- # and any ACK_FAILURES/ACK_FAILURE_LOG update inside it is lost the
83
- # moment the pipeline exits.
84
- for cat in debug_sessions threads; do
85
- while IFS= read -r slug; do
86
- [ -z "$slug" ] && continue
87
- if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --slug "$slug"; then
88
- record_ack_failure "$cat slug=$slug"
89
- fi
90
- done < <(printf '%s' "$AUDIT_JSON" | jq -r --arg cat "$cat" '.items[$cat][] | select(.scan_error | not) | .slug')
91
- done
92
-
93
- # seeds (--seed-id)
94
- while IFS= read -r seed_id; do
95
- [ -z "$seed_id" ] && continue
96
- if ! gsd_run query audit-open acknowledge --category seeds --milestone "$MILESTONE_VERSION" --seed-id "$seed_id"; then
97
- record_ack_failure "seeds seed_id=$seed_id"
98
- fi
99
- done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.seeds[] | select(.scan_error | not) | .seed_id')
100
-
101
- # todos (--filename) — the scanner caps its list to 5 entries per scan
102
- # (remainder items carry `_remainder_count`, no `filename`, and are skipped)
103
- while IFS= read -r filename; do
104
- [ -z "$filename" ] && continue
105
- if ! gsd_run query audit-open acknowledge --category todos --milestone "$MILESTONE_VERSION" --filename "$filename"; then
106
- record_ack_failure "todos filename=$filename"
107
- fi
108
- done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.todos[] | select((.scan_error or ._remainder_count) | not) | .filename')
109
-
110
- # quick_tasks (--dir) — the scanner's `slug` strips a leading
111
- # YYYYMMDD-/YYYY-MM-DD- date prefix for display; `--dir` needs the
112
- # ORIGINAL .planning/quick/<dir>/ name, so reconstruct it from `date`+`slug`.
113
- while IFS= read -r dir; do
114
- [ -z "$dir" ] && continue
115
- if ! gsd_run query audit-open acknowledge --category quick_tasks --milestone "$MILESTONE_VERSION" --dir "$dir"; then
116
- record_ack_failure "quick_tasks dir=$dir"
117
- fi
118
- done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.quick_tasks[] | select(.scan_error | not) | if .date != "" then "\(.date)-\(.slug)" else .slug end')
119
-
120
- # uat_gaps / verification_gaps / context_questions — phase-scoped
121
- # (--phase --file [--archived-milestone] when the item was found in an archived phase)
122
- for cat in uat_gaps verification_gaps context_questions; do
123
- while IFS= read -r item; do
124
- [ -z "$item" ] && continue
125
- phase=$(printf '%s' "$item" | jq -r '.phase')
126
- file=$(printf '%s' "$item" | jq -r '.file')
127
- archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty')
128
- if [ -n "$archived" ]; then
129
- if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --archived-milestone "$archived"; then
130
- record_ack_failure "$cat phase=$phase file=$file archived-milestone=$archived"
131
- fi
132
- else
133
- if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file"; then
134
- record_ack_failure "$cat phase=$phase file=$file"
135
- fi
136
- fi
137
- done < <(printf '%s' "$AUDIT_JSON" | jq -c --arg cat "$cat" '.items[$cat][] | select(.scan_error | not)')
138
- done
139
-
140
- # deferred_items — same phase-scoped identification, plus --text (the
141
- # exact bullet the audit read, which uniquely identifies the entry)
142
- while IFS= read -r item; do
143
- [ -z "$item" ] && continue
144
- phase=$(printf '%s' "$item" | jq -r '.phase')
145
- file=$(printf '%s' "$item" | jq -r '.file')
146
- text=$(printf '%s' "$item" | jq -r '.text')
147
- archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty')
148
- if [ -n "$archived" ]; then
149
- if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text" --archived-milestone "$archived"; then
150
- record_ack_failure "deferred_items phase=$phase file=$file archived-milestone=$archived"
151
- fi
152
- else
153
- if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text"; then
154
- record_ack_failure "deferred_items phase=$phase file=$file"
155
- fi
156
- fi
157
- done < <(printf '%s' "$AUDIT_JSON" | jq -c '.items.deferred_items[] | select(.scan_error | not)')
158
-
159
- if [ "$ACK_FAILURES" -gt 0 ]; then
160
- echo "ERROR: $ACK_FAILURES acknowledge call(s) failed — HALTING before milestone close. Resolve each listed item manually (e.g. edit the file directly for unsupported_heading_shape/ambiguous, or re-run the audit if a --text/--file target has since changed) and re-run /gsd:complete-milestone:" >&2
161
- printf '%s\n' "$ACK_FAILURE_LOG" >&2
162
- exit 1
163
- fi
164
- ```
165
- `todos` is the only category the scanner caps (5 entries per scan, with a remainder count for the rest). Re-run `gsd_run query audit-open --json` (through the same `@file:` handling above) and repeat the `todos` block until it reports no `todos` items — every other category always returns its full open set in one pass.
166
- 3. Re-run `gsd_run query audit-open --json` once more and write the items just acknowledged as new rows to STATE.md under `## Deferred Items` — append to the existing table (creating the section if absent) rather than overwriting it, preserving rows recorded at earlier milestone closes:
167
- ```markdown
168
- ## Deferred Items
169
-
170
- Items acknowledged and deferred at milestone close, most recent first:
171
-
172
- | Category | Item | Status | Deferred At | Milestone |
173
- |----------|------|--------|-------------|-----------|
174
- | debug_sessions | {slug} | {status} | {date} | {milestone} |
175
- | quick_tasks | {slug} | {status} | {date} | {milestone} |
176
- | threads | {slug} | {status} | {date} | {milestone} |
177
- | seeds | {seed_id} | {status} | {date} | {milestone} |
178
- | todos | {filename} | (presence-only) | {date} | {milestone} |
179
- | uat_gaps | {phase}/{file} | {status} | {date} | {milestone} |
180
- | verification_gaps | {phase}/{file} | {status} | {date} | {milestone} |
181
- | context_questions | {phase}/{file} | {question_count} questions | {date} | {milestone} |
182
- | deferred_items | {phase}/{file}: {text} | acknowledged | {date} | {milestone} |
183
- ```
184
- One row per item actually acknowledged in step 2 (omit categories with nothing to disclose this close). `{date}` is today's date; `{milestone}` is `MILESTONE_VERSION`. Sanitize all slug/status/text values via `sanitizeForDisplay()` before writing. Never inject raw file content into STATE.md.
185
- 4. Set `closeout_type=override_closeout` and record in the MILESTONES.md entry: `Known verification overrides: {N} newly acknowledged, {M} carried forward from a prior close (see STATE.md Deferred Items)` — `{N}` is the count of items acknowledged in step 2 (the pre-acknowledgment audit JSON's `counts.total`) and `{M}` is that same audit JSON's `acknowledged.total` (items a PRIOR close already suppressed and still are).
186
- 5. Proceed with milestone close.
187
-
188
- Acknowledging is verdict-preserving and self-invalidating: it never rewrites the artifact's own `status:` field (except `deferred_items`, whose entry has no other meaning for that field), and the suppression it grants lapses automatically the moment the artifact's observed state changes again — a reopened debug session, an edited UAT gap, a re-triggered seed, etc. resurfaces on its own at the next audit and must be acknowledged again.
189
-
190
- If output shows all clear (no open items): set `closeout_type=verified_closeout`. If the audit JSON's `acknowledged.total` is `0`, print `All artifact types clear.` and proceed. Otherwise the close is clean only because `{acknowledged.total}` item(s) acknowledged at an earlier milestone close are still being suppressed, not because everything was fixed this time — print `All artifact types clear ({acknowledged.total} previously acknowledged item(s) still suppressed — see STATE.md Deferred Items).` and record `Known verification overrides: 0 newly acknowledged, {acknowledged.total} carried forward from a prior close (see STATE.md Deferred Items)` in the MILESTONES.md entry before proceeding.
65
+ **If user chooses [A] (Acknowledge):** re-fetch `audit-open --json`, then acknowledge EVERY open item (across all categories — debug_sessions, threads, seeds, todos, quick_tasks, uat_gaps, verification_gaps, context_questions, deferred_items) through the `audit-open acknowledge` CLI writer, which is what actually suppresses each item starting at the next scan (the STATE.md `## Deferred Items` table is a disclosure record only). Any failed acknowledge call HALTS the close before proceeding — a refusal must never be silently discarded. After a clean pass, append one row per acknowledged item to STATE.md's `## Deferred Items` table (sanitized via `sanitizeForDisplay()`, never raw content), set `closeout_type=override_closeout`, and record a `Known verification overrides: {N} newly acknowledged, {M} carried forward` line in MILESTONES.md. Acknowledging is verdict-preserving and self-invalidating — it never rewrites the artifact's own status (except `deferred_items`), and the suppression lapses automatically the moment the artifact's state changes again (a reopened session, an edited gap, a re-triggered seed), resurfacing at the next audit.
66
+
67
+ If output shows all clear (no open items): set `closeout_type=verified_closeout` — but if any items are `acknowledged.total` from a PRIOR close, note that carried-forward suppression explicitly rather than implying everything was fixed this time.
191
68
 
69
+ <!-- gsd:protected -->
192
70
  SECURITY: Audit JSON output is structured data from the `audit-open` query handler (same JSON contract as legacy `gsd_run audit-open`) — validated and sanitized at source. The `audit-open acknowledge` writer is the only path that sets the `audit_acknowledged` suppression marker — it snapshots each artifact's current state itself from the identifiers passed on the command line, so this workflow never hand-authors the marker. When writing the STATE.md disclosure table, item identifiers, statuses, and deferred-item text are sanitized via `sanitizeForDisplay()` before inclusion. Never inject raw user-supplied content into STATE.md without sanitization.
71
+
72
+ Exact per-category bash (including the `@file:` large-payload handling, the `todos` 5-per-scan cap, and the phase-scoped `--archived-milestone` handling) and the exact STATE.md table shape: `gsd-core/workflows/complete-milestone/detail/elaboration.md` § 1.
193
73
  </step>
194
74
 
195
75
  <step name="verify_readiness">
@@ -572,8 +452,12 @@ After `milestone complete` has archived, reorganize ROADMAP.md with milestone gr
572
452
  Extract the Backlog section from the current ROADMAP.md before making any changes:
573
453
 
574
454
  ```bash
455
+ INIT_REORG=$(gsd_run query init.complete-milestone)
456
+ if [[ "$INIT_REORG" == @file:* ]]; then INIT_REORG=$(cat "${INIT_REORG#@file:}"); fi
457
+ _gsd_field() { node -e "const o=JSON.parse(process.argv[1]); const v=o[process.argv[2]]; process.stdout.write(v==null?'':String(v))" "$1" "$2"; }
458
+ ROADMAP_PATH=$(_gsd_field "$INIT_REORG" roadmap_path)
575
459
  # Extract lines under ## Backlog through end of file (or next ## section)
576
- BACKLOG_SECTION=$(awk '/^## Backlog/{found=1} found{print}' .planning/ROADMAP.md)
460
+ BACKLOG_SECTION=$(awk '/^## Backlog/{found=1} found{print}' "$ROADMAP_PATH")
577
461
  ```
578
462
 
579
463
  If `$BACKLOG_SECTION` is empty, there is no Backlog section — skip silently.
@@ -585,10 +469,15 @@ This rewrite is an *intentional* catastrophic shrink: phase detail was just arch
585
469
  1. Arm the sentinel (single-use; the guard checks it is fresh — within 15 minutes — and names exactly this file, then consumes it):
586
470
 
587
471
  ```bash
588
- printf '.planning/ROADMAP.md\n' > .planning/.gsd-allow-shrink
472
+ INIT_REORG=$(gsd_run query init.complete-milestone)
473
+ if [[ "$INIT_REORG" == @file:* ]]; then INIT_REORG=$(cat "${INIT_REORG#@file:}"); fi
474
+ _gsd_field() { node -e "const o=JSON.parse(process.argv[1]); const v=o[process.argv[2]]; process.stdout.write(v==null?'':String(v))" "$1" "$2"; }
475
+ ROADMAP_PATH=$(_gsd_field "$INIT_REORG" roadmap_path)
476
+ printf '%s\n' "$ROADMAP_PATH" > .planning/.gsd-allow-shrink
477
+ echo "Write target: $ROADMAP_PATH"
589
478
  ```
590
479
 
591
- 2. Compose the full new ROADMAP.md content (template below) and overwrite `.planning/ROADMAP.md` with the **Write tool** — the normal path. The guard allows this one shrink and deletes the sentinel. If the Write is blocked anyway, the sentinel was stale or consumed — re-run the `printf` and retry the Write.
480
+ 2. Compose the full new ROADMAP.md content (template below) and overwrite the file at **`$ROADMAP_PATH`** (the "Write target" path printed above — under an active workstream this is the workstream-scoped roadmap, NOT the literal `.planning/ROADMAP.md`) with the **Write tool** — the normal path. The guard allows this one shrink and deletes the sentinel. If the Write is blocked anyway, the sentinel was stale or consumed — re-run the `printf` and retry the Write.
592
481
 
593
482
  Template for the composed content:
594
483
 
@@ -618,15 +507,29 @@ Append the extracted Backlog content verbatim to the end of the newly written RO
618
507
  **Safety commit — commit archive files BEFORE deleting any originals:**
619
508
 
620
509
  ```bash
621
- gsd_run query commit "chore: archive v[X.Y] milestone files" --files .planning/milestones/v[X.Y]-ROADMAP.md .planning/milestones/v[X.Y]-REQUIREMENTS.md .planning/milestones/v[X.Y]-MILESTONE-AUDIT.md .planning/MILESTONES.md .planning/PROJECT.md .planning/STATE.md .planning/ROADMAP.md
510
+ INIT_REORG=$(gsd_run query init.complete-milestone)
511
+ if [[ "$INIT_REORG" == @file:* ]]; then INIT_REORG=$(cat "${INIT_REORG#@file:}"); fi
512
+ _gsd_field() { node -e "const o=JSON.parse(process.argv[1]); const v=o[process.argv[2]]; process.stdout.write(v==null?'':String(v))" "$1" "$2"; }
513
+ STATE_PATH=$(_gsd_field "$INIT_REORG" state_path)
514
+ ROADMAP_PATH=$(_gsd_field "$INIT_REORG" roadmap_path)
515
+ ARCHIVE_DIR=$(_gsd_field "$INIT_REORG" archive_dir)
516
+ MILESTONES_PATH=$(_gsd_field "$INIT_REORG" milestones_path)
517
+ PROJECT_PATH=$(_gsd_field "$INIT_REORG" project_path)
518
+ gsd_run query commit "chore: archive v[X.Y] milestone files" --files "${ARCHIVE_DIR}/v[X.Y]-ROADMAP.md" "${ARCHIVE_DIR}/v[X.Y]-REQUIREMENTS.md" "${ARCHIVE_DIR}/v[X.Y]-MILESTONE-AUDIT.md" "$MILESTONES_PATH" "$PROJECT_PATH" "$STATE_PATH" "$ROADMAP_PATH"
622
519
  ```
623
520
 
624
521
  This creates a durable checkpoint in git history. If anything fails after this point, the working tree can be reconstructed from git.
625
522
 
523
+ MILESTONES.md and PROJECT.md are workstream-scoped the same way STATE.md/ROADMAP.md are (`planningPaths(cwd).planning`/`.project`) — under an active workstream this commits the actual files `milestone complete` wrote, not the root copies.
524
+
626
525
  **Remove REQUIREMENTS.md via git rm** (preserves history, stages deletion atomically):
627
526
 
628
527
  ```bash
629
- git rm .planning/REQUIREMENTS.md
528
+ INIT_REORG=$(gsd_run query init.complete-milestone)
529
+ if [[ "$INIT_REORG" == @file:* ]]; then INIT_REORG=$(cat "${INIT_REORG#@file:}"); fi
530
+ _gsd_field() { node -e "const o=JSON.parse(process.argv[1]); const v=o[process.argv[2]]; process.stdout.write(v==null?'':String(v))" "$1" "$2"; }
531
+ REQUIREMENTS_PATH=$(_gsd_field "$INIT_REORG" requirements_path)
532
+ git rm "$REQUIREMENTS_PATH"
630
533
  ```
631
534
 
632
535
  </step>
@@ -716,140 +619,14 @@ See: .planning/PROJECT.md (updated [today])
716
619
 
717
620
  <step name="handle_branches">
718
621
 
719
- Check branching strategy and offer merge options.
720
-
721
- Use `init milestone-op` for context, or load config directly:
722
-
723
- ```bash
724
- INIT=$(gsd_run query init.execute-phase "1")
725
- if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
726
- INIT_CM=$(gsd_run query init.complete-milestone)
727
- if [[ "$INIT_CM" == @file:* ]]; then INIT_CM=$(cat "${INIT_CM#@file:}"); fi
728
- ```
729
-
730
- Extract `branching_strategy`, `phase_branch_template`, `milestone_branch_template`, and `commit_docs` from init JSON. Extract `git_create_tag` and `section_manifest` from `INIT_CM` (used by the `git_tag` step below).
731
-
732
- Detect base branch:
622
+ Check the project's `branching_strategy` (from `init.execute-phase`/`init.complete-milestone`). `"none"` skips straight to `git_tag`. For `"phase"` or `"milestone"`, list the matching branches (by the configured prefix template); no branches found also skips to `git_tag`. Resolve the base branch through the single shared resolver, never a bare `main`/`master` fallback (Issue #1146):
733
623
  ```bash
734
624
  BASE_BRANCH=$(gsd_run query git.base-branch)
735
625
  ```
736
626
 
737
- **If "none":** Skip to git_tag.
738
-
739
- **For "phase" strategy:**
740
-
741
- ```bash
742
- BRANCH_PREFIX=$(echo "$PHASE_BRANCH_TEMPLATE" | sed 's/{.*//')
743
- PHASE_BRANCHES=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ')
744
- ```
745
-
746
- **For "milestone" strategy:**
747
-
748
- ```bash
749
- BRANCH_PREFIX=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed 's/{.*//')
750
- MILESTONE_BRANCH=$(git branch --list "${BRANCH_PREFIX}*" 2>/dev/null | sed 's/^\*//' | tr -d ' ' | head -1)
751
- ```
752
-
753
- **If no branches found:** Skip to git_tag.
754
-
755
- **If branches exist:**
756
-
757
- ```
758
- ## Git Branches Detected
759
-
760
- Branching strategy: {phase/milestone}
761
- Branches: {list}
762
-
763
- Options:
764
- 1. **Merge to main** — Merge branch(es) to main
765
- 2. **Delete without merging** — Already merged or not needed
766
- 3. **Keep branches** — Leave for manual handling
767
- ```
768
-
769
- AskUserQuestion with options: Squash merge (Recommended), Merge with history, Delete without merging, Keep branches.
770
-
771
- **Squash merge:**
772
-
773
- ```bash
774
- CURRENT_BRANCH=$(git branch --show-current)
775
- git checkout ${BASE_BRANCH}
776
-
777
- if [ "$BRANCHING_STRATEGY" = "phase" ]; then
778
- # Rewrapped through unquoted command substitution (gsd-core#4109): a bare
779
- # `$VAR` word-splits under bash but not zsh, collapsing every element onto
780
- # one iteration there.
781
- for branch in $(printf '%s' "$PHASE_BRANCHES"); do
782
- git merge --squash "$branch"
783
- # Strip .planning/ from staging if commit_docs is false
784
- if [ "$COMMIT_DOCS" = "false" ]; then
785
- git reset HEAD .planning/ 2>/dev/null || true
786
- fi
787
- git commit -m "feat: $branch for v[X.Y]"
788
- done
789
- fi
790
-
791
- if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
792
- git merge --squash "$MILESTONE_BRANCH"
793
- # Strip .planning/ from staging if commit_docs is false
794
- if [ "$COMMIT_DOCS" = "false" ]; then
795
- git reset HEAD .planning/ 2>/dev/null || true
796
- fi
797
- git commit -m "feat: $MILESTONE_BRANCH for v[X.Y]"
798
- fi
799
-
800
- git checkout "$CURRENT_BRANCH"
801
- ```
802
-
803
- **Merge with history:**
804
-
805
- ```bash
806
- CURRENT_BRANCH=$(git branch --show-current)
807
- git checkout ${BASE_BRANCH}
808
-
809
- if [ "$BRANCHING_STRATEGY" = "phase" ]; then
810
- # Rewrapped through unquoted command substitution (gsd-core#4109): a bare
811
- # `$VAR` word-splits under bash but not zsh, collapsing every element onto
812
- # one iteration there.
813
- for branch in $(printf '%s' "$PHASE_BRANCHES"); do
814
- git merge --no-ff --no-commit "$branch"
815
- # Strip .planning/ from staging if commit_docs is false
816
- if [ "$COMMIT_DOCS" = "false" ]; then
817
- git reset HEAD .planning/ 2>/dev/null || true
818
- fi
819
- git commit -m "Merge branch '$branch' for v[X.Y]"
820
- done
821
- fi
822
-
823
- if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
824
- git merge --no-ff --no-commit "$MILESTONE_BRANCH"
825
- # Strip .planning/ from staging if commit_docs is false
826
- if [ "$COMMIT_DOCS" = "false" ]; then
827
- git reset HEAD .planning/ 2>/dev/null || true
828
- fi
829
- git commit -m "Merge branch '$MILESTONE_BRANCH' for v[X.Y]"
830
- fi
831
-
832
- git checkout "$CURRENT_BRANCH"
833
- ```
834
-
835
- **Delete without merging:**
836
-
837
- ```bash
838
- if [ "$BRANCHING_STRATEGY" = "phase" ]; then
839
- # Rewrapped through unquoted command substitution (gsd-core#4109): a bare
840
- # `$VAR` word-splits under bash but not zsh, collapsing every element onto
841
- # one iteration there.
842
- for branch in $(printf '%s' "$PHASE_BRANCHES"); do
843
- git branch -d "$branch" 2>/dev/null || git branch -D "$branch"
844
- done
845
- fi
846
-
847
- if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
848
- git branch -d "$MILESTONE_BRANCH" 2>/dev/null || git branch -D "$MILESTONE_BRANCH"
849
- fi
850
- ```
627
+ If branches exist, present them and ask (AskUserQuestion): **Squash merge** (recommended) / **Merge with history** / **Delete without merging** / **Keep branches**. All three merge/delete options iterate every matching branch (phase strategy) or the one milestone branch, checking out `BASE_BRANCH` first and returning to the original branch after; both merge options strip `.planning/` from staging first when `commit_docs` is false. "Keep branches" just reports them as preserved for manual handling.
851
628
 
852
- **Keep branches:** Report "Branches preserved for manual handling"
629
+ Exact bash for each of the four options (squash, history-preserving merge, delete, keep): `gsd-core/workflows/complete-milestone/detail/elaboration.md` § 2.
853
630
 
854
631
  </step>
855
632