bmad-module-skill-forge 2.2.0 → 3.0.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 (435) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +49 -49
  3. package/docs/404.md +10 -8
  4. package/docs/_data/pinned.yaml +22 -13
  5. package/docs/_internal/STABILITY.md +58 -35
  6. package/docs/agents.md +39 -53
  7. package/docs/architecture.md +65 -51
  8. package/docs/bmad-synergy.md +53 -51
  9. package/docs/campaign.md +127 -75
  10. package/docs/concepts.md +34 -28
  11. package/docs/examples.md +70 -68
  12. package/docs/forge-auto.md +34 -31
  13. package/docs/getting-started.md +118 -57
  14. package/docs/how-it-works.md +25 -21
  15. package/docs/index.md +15 -15
  16. package/docs/skill-model.md +70 -59
  17. package/docs/troubleshooting.md +135 -18
  18. package/docs/verifying-a-skill.md +83 -57
  19. package/docs/why-skf.md +24 -22
  20. package/docs/workflows.md +274 -110
  21. package/package.json +18 -10
  22. package/src/forger/preferences.yaml +8 -1
  23. package/src/knowledge/agentskills-spec.md +2 -0
  24. package/src/knowledge/ccc-bridge.md +40 -24
  25. package/src/knowledge/confidence-tiers.md +9 -5
  26. package/src/knowledge/overview.md +3 -3
  27. package/src/knowledge/progressive-capability.md +8 -8
  28. package/src/knowledge/provenance-tracking.md +9 -5
  29. package/src/knowledge/qmd-registry.md +2 -2
  30. package/src/knowledge/skf-knowledge-index.csv +2 -2
  31. package/src/knowledge/skill-lifecycle.md +18 -11
  32. package/src/knowledge/split-body-strategy.md +0 -3
  33. package/src/knowledge/tool-resolution.md +4 -2
  34. package/src/knowledge/version-paths.md +101 -15
  35. package/src/module-help.csv +3 -3
  36. package/src/module.yaml +10 -0
  37. package/src/shared/data/ast-grep-recipes.yaml +608 -0
  38. package/src/shared/data/ide-context-files.json +30 -0
  39. package/src/shared/health-check.md +32 -12
  40. package/src/shared/references/description-guard-protocol.md +12 -9
  41. package/src/shared/references/feasibility-report-schema.md +19 -13
  42. package/src/shared/references/headless-gate-convention.md +55 -3
  43. package/src/shared/references/output-contract-schema.md +19 -8
  44. package/src/shared/references/pipeline-contracts.md +94 -29
  45. package/src/shared/references/tessl-review.md +77 -0
  46. package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
  47. package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
  48. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
  49. package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
  50. package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
  51. package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
  52. package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
  53. package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
  54. package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
  55. package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
  56. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
  57. package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
  58. package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
  59. package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
  60. package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
  61. package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
  62. package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
  63. package/src/shared/scripts/skf-atomic-write.py +30 -14
  64. package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
  65. package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
  66. package/src/shared/scripts/skf-check-unit-records.py +184 -0
  67. package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
  68. package/src/shared/scripts/skf-classify-changed-files.py +694 -0
  69. package/src/shared/scripts/skf-comention-pairs.py +1333 -123
  70. package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
  71. package/src/shared/scripts/skf-count-imports.py +1867 -0
  72. package/src/shared/scripts/skf-count-tokens.py +1 -1
  73. package/src/shared/scripts/skf-derive-assembly-shape.py +3 -2
  74. package/src/shared/scripts/skf-description-guard.py +99 -11
  75. package/src/shared/scripts/skf-detect-docs.py +694 -2
  76. package/src/shared/scripts/skf-detect-language.py +486 -107
  77. package/src/shared/scripts/skf-detect-registry.py +887 -0
  78. package/src/shared/scripts/skf-detect-scripts-assets.py +1 -1
  79. package/src/shared/scripts/skf-detect-tools.py +209 -27
  80. package/src/shared/scripts/skf-detect-workspaces.py +506 -30
  81. package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
  82. package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
  83. package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
  84. package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
  85. package/src/shared/scripts/skf-extract-public-api.py +4084 -85
  86. package/src/shared/scripts/skf-extraction-inventory.py +746 -0
  87. package/src/shared/scripts/skf-extraction-snapshot.py +539 -0
  88. package/src/shared/scripts/skf-fetch-temporal.py +674 -0
  89. package/src/shared/scripts/skf-find-cycles.py +97 -29
  90. package/src/shared/scripts/skf-find-test-report.py +476 -0
  91. package/src/shared/scripts/skf-forge-tier-rw.py +444 -203
  92. package/src/shared/scripts/skf-github-fetch.py +365 -0
  93. package/src/shared/scripts/skf-github-probe.py +661 -0
  94. package/src/shared/scripts/skf-hash-content.py +166 -12
  95. package/src/shared/scripts/skf-language-corpora.py +1 -1
  96. package/src/shared/scripts/skf-load-provenance.py +159 -27
  97. package/src/shared/scripts/skf-manifest-ops.py +1 -1
  98. package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
  99. package/src/shared/scripts/skf-merge-doc-urls.py +1 -1
  100. package/src/shared/scripts/skf-names-present.py +221 -0
  101. package/src/shared/scripts/skf-pair-intersect.py +119 -28
  102. package/src/shared/scripts/skf-parse-gaps.py +1384 -0
  103. package/src/shared/scripts/skf-preapply.py +1 -1
  104. package/src/shared/scripts/skf-preflight.py +161 -27
  105. package/src/shared/scripts/skf-promote-staged.py +430 -0
  106. package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
  107. package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
  108. package/src/shared/scripts/skf-quick-batch.py +774 -0
  109. package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
  110. package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
  111. package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
  112. package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
  113. package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
  114. package/src/shared/scripts/skf-resolve-authoritative-files.py +128 -13
  115. package/src/shared/scripts/skf-resolve-package.py +523 -106
  116. package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
  117. package/src/shared/scripts/skf-run-lock.py +577 -0
  118. package/src/shared/scripts/skf-scan-manifests.py +998 -57
  119. package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
  120. package/src/shared/scripts/skf-severity-classify.py +712 -108
  121. package/src/shared/scripts/skf-shape-detect.py +298 -204
  122. package/src/shared/scripts/skf-shard-body.py +9 -8
  123. package/src/shared/scripts/skf-skill-inventory.py +2412 -89
  124. package/src/shared/scripts/skf-skills-module.py +472 -0
  125. package/src/shared/scripts/skf-source-tree.py +1796 -0
  126. package/src/shared/scripts/skf-structural-diff.py +670 -119
  127. package/src/shared/scripts/skf-tessl-review.py +706 -0
  128. package/src/shared/scripts/skf-update-active-symlink.py +5 -5
  129. package/src/shared/scripts/skf-update-run-state.py +624 -0
  130. package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
  131. package/src/shared/scripts/skf-validate-brief-schema.py +4 -1
  132. package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
  133. package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
  134. package/src/shared/scripts/skf-validate-output.py +712 -64
  135. package/src/shared/scripts/skf-validate-pins.py +1 -1
  136. package/src/shared/scripts/skf-verify-no-trace.py +116 -10
  137. package/src/shared/scripts/skf-verify-provenance-completeness.py +2919 -54
  138. package/src/shared/scripts/skf-write-skill-brief.py +367 -19
  139. package/src/shared/tool-requirements.yaml +201 -0
  140. package/src/skf-analyze-source/SKILL.md +41 -36
  141. package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
  142. package/src/skf-analyze-source/customize.toml +36 -24
  143. package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
  144. package/src/skf-analyze-source/references/continue.md +2 -13
  145. package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
  146. package/src/skf-analyze-source/references/generate-briefs.md +121 -55
  147. package/src/skf-analyze-source/references/headless-contract.md +42 -15
  148. package/src/skf-analyze-source/references/identify-units.md +96 -110
  149. package/src/skf-analyze-source/references/init.md +73 -71
  150. package/src/skf-analyze-source/references/map-and-detect.md +123 -105
  151. package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
  152. package/src/skf-analyze-source/references/recommend.md +51 -70
  153. package/src/skf-analyze-source/references/scan-project.md +60 -84
  154. package/src/skf-analyze-source/references/scan-root.md +39 -0
  155. package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
  156. package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
  157. package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
  158. package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
  159. package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
  160. package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
  161. package/src/skf-audit-skill/SKILL.md +43 -51
  162. package/src/skf-audit-skill/assets/drift-report-template.md +27 -3
  163. package/src/skf-audit-skill/customize.toml +41 -20
  164. package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
  165. package/src/skf-audit-skill/references/doc-drift.md +141 -0
  166. package/src/skf-audit-skill/references/headless-contract.md +29 -0
  167. package/src/skf-audit-skill/references/init.md +148 -210
  168. package/src/skf-audit-skill/references/re-index.md +96 -108
  169. package/src/skf-audit-skill/references/report.md +96 -33
  170. package/src/skf-audit-skill/references/semantic-diff.md +27 -18
  171. package/src/skf-audit-skill/references/severity-classify.md +79 -65
  172. package/src/skf-audit-skill/references/severity-rules.md +15 -6
  173. package/src/skf-audit-skill/references/structural-diff.md +75 -90
  174. package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
  175. package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
  176. package/src/skf-brief-skill/SKILL.md +36 -25
  177. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  178. package/src/skf-brief-skill/assets/scope-templates.md +17 -24
  179. package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
  180. package/src/skf-brief-skill/customize.toml +48 -20
  181. package/src/skf-brief-skill/references/analyze-target.md +142 -125
  182. package/src/skf-brief-skill/references/confirm-brief.md +10 -8
  183. package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
  184. package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
  185. package/src/skf-brief-skill/references/gather-intent.md +87 -182
  186. package/src/skf-brief-skill/references/headless-args.md +84 -16
  187. package/src/skf-brief-skill/references/health-check.md +1 -1
  188. package/src/skf-brief-skill/references/invocation-contract.md +18 -13
  189. package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
  190. package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
  191. package/src/skf-brief-skill/references/scope-definition.md +123 -47
  192. package/src/skf-brief-skill/references/step-auto-brief.md +24 -80
  193. package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
  194. package/src/skf-brief-skill/references/version-resolution.md +3 -2
  195. package/src/skf-brief-skill/references/write-brief.md +66 -57
  196. package/src/skf-campaign/SKILL.md +31 -34
  197. package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
  198. package/src/skf-campaign/customize.toml +47 -16
  199. package/src/skf-campaign/references/campaign-contracts.md +39 -29
  200. package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
  201. package/src/skf-campaign/references/health-check.md +7 -16
  202. package/src/skf-campaign/references/step-01-setup.md +46 -84
  203. package/src/skf-campaign/references/step-02-strategy.md +31 -30
  204. package/src/skf-campaign/references/step-03-pins.md +26 -18
  205. package/src/skf-campaign/references/step-04-provenance.md +24 -23
  206. package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
  207. package/src/skf-campaign/references/step-06-batch.md +41 -34
  208. package/src/skf-campaign/references/step-07-capstone.md +40 -25
  209. package/src/skf-campaign/references/step-08-verify.md +28 -29
  210. package/src/skf-campaign/references/step-09-refine.md +29 -30
  211. package/src/skf-campaign/references/step-10-export.md +48 -36
  212. package/src/skf-campaign/references/step-11-maintenance.md +31 -34
  213. package/src/skf-campaign/references/step-resume.md +37 -68
  214. package/src/skf-campaign/scripts/campaign-deps.py +57 -18
  215. package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
  216. package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
  217. package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
  218. package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
  219. package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
  220. package/src/skf-campaign/scripts/campaign-report.py +204 -37
  221. package/src/skf-campaign/scripts/campaign-state.py +1128 -0
  222. package/src/skf-campaign/scripts/campaign-status.py +10 -74
  223. package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
  224. package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
  225. package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
  226. package/src/skf-campaign/templates/campaign-report-template.md +5 -1
  227. package/src/skf-campaign/templates/kickoff-template.md +0 -14
  228. package/src/skf-create-skill/SKILL.md +27 -20
  229. package/src/skf-create-skill/assets/compile-assembly-rules.md +6 -5
  230. package/src/skf-create-skill/assets/skill-sections.md +15 -5
  231. package/src/skf-create-skill/customize.toml +20 -8
  232. package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
  233. package/src/skf-create-skill/references/batch-mode.md +81 -0
  234. package/src/skf-create-skill/references/ccc-index-check.md +27 -0
  235. package/src/skf-create-skill/references/compile.md +107 -29
  236. package/src/skf-create-skill/references/component-extraction.md +113 -91
  237. package/src/skf-create-skill/references/enrich.md +41 -23
  238. package/src/skf-create-skill/references/entry-points-by-hand.md +20 -0
  239. package/src/skf-create-skill/references/extract.md +200 -161
  240. package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
  241. package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
  242. package/src/skf-create-skill/references/extraction-patterns.md +584 -196
  243. package/src/skf-create-skill/references/generate-artifacts.md +145 -93
  244. package/src/skf-create-skill/references/health-check.md +1 -1
  245. package/src/skf-create-skill/references/load-brief.md +53 -36
  246. package/src/skf-create-skill/references/relabel-rule.md +12 -0
  247. package/src/skf-create-skill/references/report.md +28 -59
  248. package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
  249. package/src/skf-create-skill/references/step-auto-shard.md +20 -93
  250. package/src/skf-create-skill/references/step-doc-rot.md +44 -60
  251. package/src/skf-create-skill/references/step-doc-sources.md +28 -19
  252. package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
  253. package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
  254. package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
  255. package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
  256. package/src/skf-create-skill/references/validate.md +173 -99
  257. package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
  258. package/src/skf-create-stack-skill/SKILL.md +29 -56
  259. package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
  260. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
  261. package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
  262. package/src/skf-create-stack-skill/customize.toml +23 -12
  263. package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
  264. package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
  265. package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
  266. package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
  267. package/src/skf-create-stack-skill/references/generate-output.md +138 -128
  268. package/src/skf-create-stack-skill/references/init.md +90 -32
  269. package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
  270. package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
  271. package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
  272. package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
  273. package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
  274. package/src/skf-create-stack-skill/references/report.md +39 -33
  275. package/src/skf-create-stack-skill/references/validate.md +20 -39
  276. package/src/skf-drop-skill/SKILL.md +28 -51
  277. package/src/skf-drop-skill/customize.toml +44 -30
  278. package/src/skf-drop-skill/references/execute.md +79 -146
  279. package/src/skf-drop-skill/references/invocation-contract.md +64 -0
  280. package/src/skf-drop-skill/references/report.md +46 -30
  281. package/src/skf-drop-skill/references/select.md +118 -124
  282. package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
  283. package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
  284. package/src/skf-export-skill/SKILL.md +40 -62
  285. package/src/skf-export-skill/assets/managed-section-format.md +14 -50
  286. package/src/skf-export-skill/assets/snippet-format.md +3 -1
  287. package/src/skf-export-skill/customize.toml +42 -13
  288. package/src/skf-export-skill/references/generate-snippet.md +54 -46
  289. package/src/skf-export-skill/references/health-check.md +2 -0
  290. package/src/skf-export-skill/references/invocation-contract.md +15 -0
  291. package/src/skf-export-skill/references/load-skill.md +88 -62
  292. package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
  293. package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
  294. package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
  295. package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
  296. package/src/skf-export-skill/references/package.md +15 -73
  297. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
  298. package/src/skf-export-skill/references/result-envelope.md +31 -16
  299. package/src/skf-export-skill/references/summary.md +41 -17
  300. package/src/skf-export-skill/references/token-report.md +4 -12
  301. package/src/skf-export-skill/references/update-context.md +118 -237
  302. package/src/skf-forger/SKILL.md +61 -37
  303. package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
  304. package/src/skf-forger/customize.toml +43 -0
  305. package/src/skf-forger/references/pipeline-mode.md +73 -18
  306. package/src/skf-forger/scripts/forge-status.py +299 -0
  307. package/src/skf-forger/scripts/parse-pipeline.py +322 -54
  308. package/src/skf-forger/scripts/pipeline-gate.py +428 -0
  309. package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
  310. package/src/skf-quick-skill/SKILL.md +54 -53
  311. package/src/skf-quick-skill/assets/skill-template.md +4 -2
  312. package/src/skf-quick-skill/customize.toml +51 -20
  313. package/src/skf-quick-skill/references/batch-mode.md +74 -21
  314. package/src/skf-quick-skill/references/compile.md +30 -31
  315. package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
  316. package/src/skf-quick-skill/references/finalize.md +69 -18
  317. package/src/skf-quick-skill/references/halt-contract.md +41 -29
  318. package/src/skf-quick-skill/references/health-check.md +5 -3
  319. package/src/skf-quick-skill/references/quick-extract.md +114 -45
  320. package/src/skf-quick-skill/references/registry-resolution.md +9 -64
  321. package/src/skf-quick-skill/references/resolve-target.md +116 -66
  322. package/src/skf-quick-skill/references/write-and-validate.md +79 -40
  323. package/src/skf-refine-architecture/SKILL.md +43 -53
  324. package/src/skf-refine-architecture/customize.toml +49 -20
  325. package/src/skf-refine-architecture/references/compile.md +97 -54
  326. package/src/skf-refine-architecture/references/exit-codes.md +30 -0
  327. package/src/skf-refine-architecture/references/finding-storage.md +21 -0
  328. package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
  329. package/src/skf-refine-architecture/references/improvements.md +14 -20
  330. package/src/skf-refine-architecture/references/init.md +98 -57
  331. package/src/skf-refine-architecture/references/issue-detection.md +32 -39
  332. package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
  333. package/src/skf-refine-architecture/references/report.md +65 -54
  334. package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
  335. package/src/skf-rename-skill/SKILL.md +19 -54
  336. package/src/skf-rename-skill/customize.toml +32 -26
  337. package/src/skf-rename-skill/references/execute.md +230 -184
  338. package/src/skf-rename-skill/references/exit-codes.md +7 -7
  339. package/src/skf-rename-skill/references/health-check.md +1 -7
  340. package/src/skf-rename-skill/references/invocation-contract.md +25 -0
  341. package/src/skf-rename-skill/references/report.md +63 -52
  342. package/src/skf-rename-skill/references/select.md +197 -115
  343. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
  344. package/src/skf-setup/SKILL.md +26 -26
  345. package/src/skf-setup/customize.toml +14 -10
  346. package/src/skf-setup/references/auto-index.md +50 -63
  347. package/src/skf-setup/references/ccc-index.md +29 -90
  348. package/src/skf-setup/references/detect-and-tier.md +58 -67
  349. package/src/skf-setup/references/health-check.md +2 -0
  350. package/src/skf-setup/references/invocation-contract.md +14 -0
  351. package/src/skf-setup/references/report.md +43 -166
  352. package/src/skf-setup/references/tier-rules.md +5 -5
  353. package/src/skf-setup/references/write-config.md +34 -57
  354. package/src/skf-test-skill/SKILL.md +17 -57
  355. package/src/skf-test-skill/assets/output-section-formats.md +33 -34
  356. package/src/skf-test-skill/customize.toml +27 -13
  357. package/src/skf-test-skill/references/coherence-check.md +134 -62
  358. package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
  359. package/src/skf-test-skill/references/coverage-check.md +217 -254
  360. package/src/skf-test-skill/references/detect-mode.md +5 -5
  361. package/src/skf-test-skill/references/external-validators.md +94 -82
  362. package/src/skf-test-skill/references/health-check.md +6 -7
  363. package/src/skf-test-skill/references/init.md +163 -88
  364. package/src/skf-test-skill/references/invocation-contract.md +66 -0
  365. package/src/skf-test-skill/references/migration-section-rules.md +5 -2
  366. package/src/skf-test-skill/references/report.md +162 -142
  367. package/src/skf-test-skill/references/score.md +80 -107
  368. package/src/skf-test-skill/references/scoring-rules.md +41 -155
  369. package/src/skf-test-skill/references/source-access-protocol.md +19 -92
  370. package/src/skf-test-skill/references/step-hard-gate.md +51 -33
  371. package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
  372. package/src/skf-test-skill/scripts/build-result-context.py +287 -0
  373. package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
  374. package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
  375. package/src/skf-test-skill/scripts/compute-score.py +223 -45
  376. package/src/skf-test-skill/scripts/gap-ledger.py +1381 -0
  377. package/src/skf-test-skill/scripts/hard-gate.py +203 -0
  378. package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
  379. package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
  380. package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
  381. package/src/skf-test-skill/scripts/score-signatures.py +459 -0
  382. package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
  383. package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
  384. package/src/skf-test-skill/scripts/verify-declared-numerator.py +127 -52
  385. package/src/skf-test-skill/templates/test-report-template.md +21 -15
  386. package/src/skf-update-skill/SKILL.md +38 -25
  387. package/src/skf-update-skill/customize.toml +27 -12
  388. package/src/skf-update-skill/references/detect-changes.md +233 -222
  389. package/src/skf-update-skill/references/gap-driven.md +336 -0
  390. package/src/skf-update-skill/references/health-check.md +31 -5
  391. package/src/skf-update-skill/references/init.md +250 -84
  392. package/src/skf-update-skill/references/invocation-contract.md +12 -0
  393. package/src/skf-update-skill/references/manual-section-rules.md +6 -23
  394. package/src/skf-update-skill/references/merge.md +180 -47
  395. package/src/skf-update-skill/references/re-extract.md +53 -262
  396. package/src/skf-update-skill/references/report.md +100 -35
  397. package/src/skf-update-skill/references/write.md +331 -149
  398. package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
  399. package/src/skf-verify-stack/SKILL.md +23 -42
  400. package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
  401. package/src/skf-verify-stack/customize.toml +55 -22
  402. package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
  403. package/src/skf-verify-stack/references/coverage.md +67 -64
  404. package/src/skf-verify-stack/references/exit-codes.md +21 -8
  405. package/src/skf-verify-stack/references/init.md +107 -62
  406. package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
  407. package/src/skf-verify-stack/references/integrations.md +106 -65
  408. package/src/skf-verify-stack/references/report.md +59 -42
  409. package/src/skf-verify-stack/references/requirements.md +32 -9
  410. package/src/skf-verify-stack/references/synthesize.md +53 -39
  411. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
  412. package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
  413. package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
  414. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
  415. package/tools/cli/commands/install.js +11 -4
  416. package/tools/cli/commands/status.js +15 -3
  417. package/tools/cli/commands/update.js +11 -4
  418. package/tools/cli/lib/tool-check.js +320 -0
  419. package/tools/cli/lib/version-check.js +22 -9
  420. package/tools/cli/skf-cli.js +4 -1
  421. package/.gitattributes +0 -16
  422. package/.nvmrc +0 -1
  423. package/docs/_internal/RELEASING.md +0 -679
  424. package/release-audits/v1.0.0-launch-audit.md +0 -728
  425. package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
  426. package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
  427. package/src/skf-campaign/manifest.yaml +0 -15
  428. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
  429. package/src/skf-create-skill/references/ecosystem-check.md +0 -74
  430. package/src/skf-drop-skill/references/headless-contract.md +0 -18
  431. package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
  432. package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
  433. package/src/skf-update-skill/references/validate.md +0 -124
  434. package/tools/validate-docs-drift.js +0 -233
  435. package/tools/validate-docs-links.js +0 -665
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Campaign Contracts
4
4
 
5
- The self-contained contracts consulted at a specific moment — when a step HALTs, mutates state, or emits the final headless envelope. Each stands alone so it survives context compaction.
5
+ The self-contained contracts consulted at a specific moment: when a step HALTs, writes state or the decision log, or emits a headless progress event. Each stands alone so it survives context compaction.
6
6
 
7
7
  ## Exit Codes
8
8
 
@@ -10,51 +10,61 @@ Every HARD HALT exits with a stable, documented code so headless automators can
10
10
 
11
11
  | Code | Meaning | Raised by |
12
12
  | ---- | -------------------- | ----------------------------------------------------------- |
13
- | 0 | success | step-11 (terminal); step-resume §3–§4 (campaign already complete — nothing to resume) |
14
- | 2 | invalid-input | step-01 §1 (no targets, or a malformed `--manifest` line); steps 02/03/04/06 §4 (a helper reports unreadable input or a required tool such as `gh` unavailable); step-resume §1/§3 (resume targets a missing campaign or unknown skill) |
15
- | 3 | invalid-state | any step §1 (`campaign-validate-state.py` non-zero on load) |
16
- | 4 | circular-deps | step-02 §5 (a dependency cycle, or a dangling `depends_on` reference — either way the graph cannot be ordered) |
17
- | 5 | invalid-pin | step-03 §5 |
18
- | 6 | inaccessible-repo | step-04 §5 |
19
- | 7 | dependency-deadlock | step-05 §4 (no skill ready and no recovery chosen) |
20
- | 8 | missing-brief | step-03/04/05 §2 and step-06 §4 (brief missing/unreadable, or a Tier B target has no matching brief entry) |
13
+ | 0 | success | step-11 (terminal: the success line, which the health check displays last); step-resume §3 (campaign already complete: nothing to resume, and in headless mode the success line again). Never an error halt: no `emit-halt` |
14
+ | 2 | invalid-input | step-01 §1 (no targets, or a target the parse script rejects: a malformed manifest line or brief target, a `repo_url` that is no GitHub repository, a name that is no skill name, a Tier A pin that is no X.Y.Z version, a duplicate name) and §2 (a quality gate the gate script rejects, a brief it cannot read, or a state file or backup that already exists); steps 02/03/04 §3 and step-06 §3 (a helper reports unreadable input or a required tool such as `gh` unavailable); step-05 §4 and §5, step-07 §2 and step-10 §3 (a directive override that breaks the quality gate, or a directive the gate script cannot read); step-05 §5 (the kickoff script cannot read its template, state, directive or a facts file, or a `file:` fact path names no file); step-resume §1/§3 (resume targets a missing campaign or unknown skill) |
15
+ | 3 | invalid-state | any step §1 (`campaign-validate-state.py` non-zero on load), and any `campaign-state.py` write that exits 3 (the state, or the change, fails validation) |
16
+ | 4 | circular-deps | step-02 §4 (a dependency cycle, a dangling `depends_on` reference, or a Tier A skill that depends on a Tier B skill: either way the plan cannot be followed) and §7 (`apply-plan` refuses the plan) |
17
+ | 5 | invalid-pin | step-03 §4 and §5 (`apply-pins` refuses an invalid pin) |
18
+ | 6 | inaccessible-repo | step-04 §4 and §5 (`apply-provenance` refuses an inaccessible repo) |
19
+ | 7 | dependency-deadlock | step-05 §4 (no skill ready and no recovery chosen; defensive, since Strategy rejects every order the loop cannot follow) |
20
+ | 8 | missing-brief | step-03/04/05 §2, step-05 §4 and §5, step-06 §3 (brief missing/unreadable, or a skill has no matching brief target), and step-resume §3 (Setup stopped before it wrote the brief) |
21
21
  | 9 | corrupt-state | step-resume §1 (primary unrecoverable, `.bak` also invalid) |
22
22
  | 10 | report-failure | step-11 §2 — **degraded only**: the report could not be generated; the campaign still completes and state stays intact (never a hard halt that discards a finished campaign) |
23
23
  | 11 | export-cancelled | step-10 §4 (operator chose `[C]ancel` — graceful, resumable) |
24
24
  | 12 | user-cancelled | any interactive gate (operator typed `cancel` / `exit` / `:q` at a prompt between Setup and the Export gate — graceful, resumable) |
25
+ | 13 | dependency-blocked | step-05 §4 (`[H]alt` at a skill whose dependency has not completed: the operator's choice, or the headless default while a dependency is still pending or active; graceful, resumable) |
25
26
 
26
27
  ## Result Contract on HARD HALT
27
28
 
28
- In addition to the success-variant envelope (see Campaign Headless Envelope below), every HARD HALT emits an **error variant** so automators don't silently break. Emit one line on **stderr**:
29
+ `shared/scripts/schemas/skf-campaign-result-envelope.v1.json` (in the SKF module: `{project-root}/_bmad/skf/` when installed, `{project-root}/src/` during development) is the one definition of the `SKF_CAMPAIGN_RESULT_JSON` envelope, and the shared emitter, `{emitEnvelopeHelper}`, is the only thing that prints it. That helper is `{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py` when installed, `{project-root}/src/shared/scripts/skf-emit-result-envelope.py` during development. Exit code 0 is never an error halt: step-11 builds the success line, and step-resume prints it again for a campaign already complete. Every HARD HALT in headless mode emits the error variant, so automators don't silently break:
29
30
 
30
- ```
31
- SKF_CAMPAIGN_RESULT_JSON: {"status":"error","exit_code":<N>,"phase":"<slug>","error":{"code":"<class>","message":"<short>"},"skills_completed":N,"skills_failed":N,"campaign_report_path":null,"decision_log":"<path-or-null>"}
31
+ ```bash
32
+ uv run scripts/campaign-state.py halt-payload --state-file {campaignWorkspacePath}/_campaign-state.yaml --phase <step slug> --halt-reason <class> <<'SKF_CAMPAIGN_HALT' | uv run {emitEnvelopeHelper} emit-halt --workflow skf-campaign --target stderr
33
+ <the halt message>
34
+ SKF_CAMPAIGN_HALT
32
35
  ```
33
36
 
34
- `<class>` is the Exit Codes meaning (e.g. `circular-deps`, `inaccessible-repo`); `<slug>` is the step where the HALT occurred. One line, no pretty-print.
37
+ `<class>` is the Exit Codes meaning (e.g. `circular-deps`, `inaccessible-repo`), from which the emitter derives `exit_code`; `<step slug>` is the kebab portion of the step file where the HALT occurred. `halt-payload` adds the `completed` and `failed` counts of the state (0 before Setup wrote it) and the decision log (null before the campaign workspace exists). The emitter prints one line on stderr, with `status` `error`, `exit_code`, `halt_reason`, `phase` and an `error` object holding `code` and `message`. If either command exits non-zero, display the halt message alone.
35
38
 
36
39
  ## State Contract
37
40
 
38
- All state mutations follow the read-backup-modify-write pattern:
39
-
40
- 1. **Read** `_campaign-state.yaml`
41
- 2. **Validate** via `uv run scripts/campaign-validate-state.py --state-file {stateFile}` (halt on invalid)
42
- 3. **Backup** — copy current `_campaign-state.yaml` to `_campaign-state.yaml.bak`
43
- 4. **Modify** in memory
44
- 5. **Update** `campaign.last_updated` to current ISO-8601 timestamp
45
- 6. **Write** modified state back to `_campaign-state.yaml`
41
+ Every write to `_campaign-state.yaml` goes through `scripts/campaign-state.py`. Each write reads the state and validates it (an invalid state is never rotated into `.bak`, and nothing is written), applies the operation, stamps `campaign.last_updated` (and any `started_at` or `completed_at` the operation sets) from the clock in UTC, validates the result (a change that would make the state invalid is refused), copies the valid primary it read to `_campaign-state.yaml.bak`, and writes the new state to a temporary file renamed over the old one. A crash therefore leaves the old file or the new one, never half of one, and `.bak` always holds the last valid state. `--stage N` on a write also sets `campaign.current_stage`: each stage writes its number only in its final write, after every gate, because resume starts at `current_stage + 1`.
46
42
 
47
- The `.bak` file is one-deep (overwritten on every write). If the primary file is corrupted (crash during write), the `.bak` file contains the last valid state — step-resume §1 recovers from it automatically rather than dead-halting.
43
+ | Operation | Step | What it writes |
44
+ | --- | --- | --- |
45
+ | `init` | step-01 | the state, from `campaign-parse-manifest.py`'s targets and the gate `campaign-quality-gate.py check` settles, then `campaign-brief.yaml` from the same targets (`--brief-out`); refuses a state or backup that exists |
46
+ | `apply-plan` | step-02 | `dependency_graph` from `campaign-deps.py --compute` |
47
+ | `apply-pins` | step-03 | each resolved pin, from `campaign-validate-pins.py`'s output |
48
+ | `apply-provenance` | step-04 | each `commit_sha`, from `campaign-provenance.py`'s output |
49
+ | `set-skill` | step-05, step-10, step-resume | a skill's `status`, `quality_score`, `skill_path`, `brief_path` or `export` outcome (`--export`); `active` stamps `started_at` once, `completed` stamps `completed_at`, `pending` clears both and the `export` outcome |
50
+ | `append-workarounds` | step-05 | entries of a skill's `workarounds_applied`, each once |
51
+ | `apply-batch` | step-06 | the Tier B batch from its line-to-skill map: `--start`, `--results-file` or `--no-results` |
52
+ | `set-campaign` | steps 07 to 09 | `architecture_doc_path`, and the capstone, verification or refinement summary read from the sub-skill's envelope line (`capstone.verified` with the verification) |
53
+ | `set-stage` | steps 05, 10, 11 | `current_stage` alone |
54
+ | `recover` | step-resume | `.bak` over the primary, when `.bak` validates (exit 9 otherwise) |
55
+ | `archive` | SKILL.md (overwrite) | nothing: it moves the state, its `.bak` and the brief into a new folder under `archive/` and returns it |
56
+ | `resume` | step-resume | nothing: it returns the stage and step file to resume |
57
+ | `halt-payload` | any HARD HALT | nothing: it prints the error envelope's payload (Result Contract above) |
48
58
 
49
- ## Campaign Headless Envelope
59
+ Its exit codes are the campaign's: 2 for invalid input (an unknown skill, an input file it cannot read, a script output that holds an error), 3 for an invalid state or change, 4 for a plan `apply-plan` refuses, 5 for a pin `apply-pins` refuses, 6 for a repository `apply-provenance` refuses, 9 for a `recover` with no valid backup. HALT with the same code.
50
60
 
51
- When `{headless_mode}` is true, the final step emits a single-line JSON envelope on stdout:
61
+ ## Decision Log
52
62
 
53
- ```
54
- SKF_CAMPAIGN_RESULT_JSON: {"status":"success|error","skills_completed":0,"skills_failed":0,"quality_scores":{},"campaign_report_path":"","decision_log":"","duration":""}
55
- ```
63
+ `_campaign-decision-log.md` is append-only, one typed and timestamped line per entry, written with `uv run scripts/campaign-state.py log --log-file {campaignWorkspacePath}/_campaign-decision-log.md --type <decision|auto|event> --text '<entry>'` (single-quote the entry, so the shell expands no `$` or backtick in it, and write an apostrophe inside it as `'\''`). Its types:
56
64
 
57
- `status` is `"success"` when the campaign completes normally, `"error"` on any unrecoverable halt (with `exit_code` per the Exit Codes table — see Result Contract on HARD HALT above). `skills_completed` and `skills_failed` count per-skill outcomes. `quality_scores` maps skill names to their test-skill scores. `campaign_report_path` points to the generated `campaign-report.md`. `decision_log` points to `_campaign-decision-log.md`. `duration` is the wall-clock time of the campaign run. Populate the counts, `quality_scores`, and `duration` directly from the `campaign-report.py` result JSON (step-11 §2) — do not recompute them by hand.
65
+ - `decision`: the operator's choice at a gate (skip or force, overwrite, export or cancel, re-run or next, a cancel)
66
+ - `auto`: the default a headless run took at a gate
67
+ - `event`: what happened without a choice: a failed skill and why, a skip the directive or a dependency forced, a recovery from the backup, a report or hook failure
58
68
 
59
69
  ## Headless Progress Events
60
70
 
@@ -64,4 +74,4 @@ When `{headless_mode}` is true, emit a single-line JSON progress event to **stde
64
74
  - exit (just before chaining): `{"stage":N,"name":"<slug>","status":"done"}`
65
75
  - on HARD HALT: `{"stage":N,"name":"<slug>","status":"halt","exit":<code>}` instead of `"done"`
66
76
 
67
- `N` is the 0-indexed stage number (0–10) and `<slug>` is the kebab portion of the step filename. For the non-numbered routing/terminal steps (`resume`, `health-check`) emit `"stage":null` with the slug. One line per event; do not pretty-print.
77
+ `N` is the 0-indexed stage number (0 to 10) and `<slug>` is the kebab portion of the step filename. For the non-numbered routing/terminal steps (`resume`, `health-check`) emit `"stage":null` with the slug. One line per event; do not pretty-print.
@@ -16,11 +16,13 @@ required; default filename `_campaign-directive.md`.
16
16
 
17
17
  All optional — the directive may hold any combination of these, or none:
18
18
 
19
- - **`## Quality Overrides`** — operator adjustments to quality gates for specific skills or the whole campaign.
20
- - **`## Skip List`** — skills to skip during processing, with rationale.
19
+ - **`## Quality Overrides`**: operator adjustments to the quality gate, one list item each. `- soft_target: 85` and `- soft_fallback: 75` set the campaign-wide values, and `- <skill>: soft_target 80, soft_fallback 70` sets one skill's (either key alone is fine). A skill's line beats a campaign-wide line, and both beat the campaign brief and customize.toml. The hard gate cannot be overridden. They apply to each skill's test threshold (skill loop) and to the export classification (Capstone and Export).
20
+ - **`## Skip List`**: skills to skip during processing, one list item each: `- <skill>: <reason>`, the reason optional. The skill loop and the Tier B batch mark a listed skill `skipped` before it runs and log the reason.
21
21
  - **`## Pipeline Flags`** — per-skill or campaign-wide pipeline modifiers.
22
22
  - **`## Notes`** — free-form operator context for the agent processing the campaign.
23
23
 
24
+ `scripts/campaign-quality-gate.py` reads the Quality Overrides and Skip List sections (a section runs to the next `#` or `##` heading; backticks around a name are dropped), so their effect never depends on how a step reads the prose. A list item in them it cannot read is reported and logged, never applied, and a name that is no skill in the campaign is logged as a warning.
25
+
24
26
  Any heading not listed above is treated as general guidance: read it and apply
25
27
  judgment based on the content. This lets operators add ad-hoc context without
26
28
  modifying this specification.
@@ -3,7 +3,6 @@
3
3
  # ({project-root}/_bmad/skf/ when installed, {project-root}/src/ during
4
4
  # development), NOT relative to this step file.
5
5
  nextStepFile: 'shared/health-check.md'
6
- stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
7
6
  ---
8
7
 
9
8
  <!-- Config: communicate in {communication_language}. -->
@@ -12,24 +11,16 @@ stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
12
11
 
13
12
  ## STEP GOAL:
14
13
 
15
- Apply the operator's findings-routing consent, then chain to the shared workflow self-improvement health check at `{nextStepFile}`. This is the terminal step of skf-campaign — after the shared health check completes, the workflow is fully done.
14
+ Chain to the shared workflow self-improvement health check at `{nextStepFile}`. This is the terminal step of skf-campaign: after the shared health check completes, the workflow is fully done.
15
+
16
+ In headless mode step 11 bound `{result_envelope_line}` to the campaign's `SKF_CAMPAIGN_RESULT_JSON` line without displaying it: the shared health check displays that line verbatim as the run's last line wherever it stops, so a `claude -p` caller reads the envelope as the final message.
16
17
 
17
18
  ## Rules
18
19
 
19
- - No user-facing reports, file writes, or result contracts in this step — those belong in step 11.
20
- - The ONLY processing permitted here is reading the routing preference (below) and carrying it into `{nextStepFile}`; otherwise delegate directly with no additional commentary.
20
+ - No user-facing reports, file writes, or result contracts in this step: those belong in step 11
21
+ - Delegate directly to `{nextStepFile}` with no additional commentary
22
+ - Do not attempt any other action between loading this step and executing `{nextStepFile}`
21
23
 
22
24
  ## MANDATORY SEQUENCE
23
25
 
24
- ### 1. Apply findings-routing consent
25
-
26
- Read `campaign.health_findings_queue` from `{stateFile}` and carry it into the shared health check as the operator's **already-decided** routing consent (so the shared step does not re-prompt, satisfying its "explicit opt-in" requirement for non-bug findings):
27
-
28
- - `"improvement"` — the operator opted in at setup: route this campaign's friction/gap findings to the shared improvement queue (the shared step's opt-in is pre-satisfied; do not re-prompt).
29
- - `"local"` (default) — keep findings in the local queue only; do not submit to the shared queue.
30
-
31
- If the state file is unreadable, default to `"local"` (never submit without consent).
32
-
33
- ### 2. Delegate
34
-
35
- Load `{nextStepFile}`, read it fully, then execute it — applying the routing consent from step 1.
26
+ Load `{nextStepFile}`, read it fully, then execute it.
@@ -3,8 +3,9 @@ nextStepFile: 'step-02-strategy.md'
3
3
  stateSchemaFile: 'assets/campaign-state-schema.json'
4
4
  stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
5
5
  briefFile: '{campaignWorkspacePath}/campaign-brief.yaml'
6
+ decisionLogFile: '{campaignWorkspacePath}/_campaign-decision-log.md'
6
7
  templateFile: '{briefTemplatePath}'
7
- validateScript: 'scripts/campaign-validate-state.py'
8
+ stateScript: 'scripts/campaign-state.py'
8
9
  manifestScript: 'scripts/campaign-parse-manifest.py'
9
10
  ---
10
11
 
@@ -16,99 +17,61 @@ manifestScript: 'scripts/campaign-parse-manifest.py'
16
17
 
17
18
  Collect campaign inputs from the operator, create the initial `_campaign-state.yaml`, and generate `campaign-brief.yaml` so the campaign has a persistent starting point that survives context death.
18
19
 
19
- This is the only step that creates the state file (it does not yet exist). All subsequent steps use **read-backup-modify-write** per the State Contract in `references/campaign-contracts.md`.
20
-
21
20
  ## RULES
22
21
 
23
- - This step creates the state file — there is no existing state to read or back up.
24
- - Validate the written state with `uv run {validateScript} --state-file {stateFile}` before generating the brief. HALT (exit code 3, `invalid-state`) on non-zero, surfacing the script's `errors[]`.
25
- - If `{headless_mode}` is true, draw inputs from `--brief`/`--manifest` (On Activation step 4) and auto-proceed through confirmation gates with the default action, logging each auto-decision to the decision log.
22
+ - This is the only step that creates the state file, with the helper's `init` operation (`{stateScript}`); every later write goes through `{stateScript}` too (State Contract in `references/campaign-contracts.md`), so no target, timestamp or gate value is ever typed into the state or the brief by hand.
23
+ - If `{headless_mode}` is true, draw inputs from `--brief`/`--manifest` (On Activation step 4) and auto-proceed through confirmation gates with the default action, logging each auto-decision with `uv run {stateScript} log --log-file {decisionLogFile} --type auto --text '<entry>'`; emit this stage's progress events, and at any HARD HALT the error envelope, per `references/campaign-contracts.md`.
26
24
 
27
25
  ## TASKS
28
26
 
29
- ### §1 — Collect Inputs
30
-
31
- Accept from the operator (or, in headless mode, from the `--brief`/`--manifest` source parsed in On Activation):
32
-
33
- - `campaign_name` — string identifier for this campaign run
34
- - Target libraries — each entry requires:
35
- - `name` — skill name
36
- - `repo_url` — source repository URL
37
- - `tier` — `"A"` (full pipeline) or `"B"` (batch)
38
- - `pin` — version pin (string) or `null` for latest
39
- - `depends_on` — array of skill names this target depends on (may be empty)
40
- - `directive_path` (optional) — path to a `_campaign-directive.md` file with operator directives (contract: `references/campaign-directive-spec.md`)
41
- - `architecture_doc_path` (optional) — path to the architecture document the verify (Stage 7) and refine (Stage 8) stages consume. If omitted here, those stages discover it at runtime (`docs/architecture.md`, then `_bmad-output/planning-artifacts/architecture.md`). Capturing it now persists the choice across resume and avoids re-prompting.
42
-
43
- When seeding from `--manifest`, parse it deterministically: `uv run {manifestScript} <manifest-file>`. If the result's `errors[]` is non-empty (script exit 1), HALT (exit code 2, `invalid-input`) listing the offending line numbers — never run a partial target set. When seeding from `--brief`, read the existing `campaign-brief.yaml` directly.
44
-
45
- If no targets can be collected (empty interactive input or empty `--brief`/`--manifest`), HALT (exit code 2, `invalid-input`) with guidance — a campaign needs at least one target.
46
-
47
- ### §2 — Health Queue Preference
48
-
49
- Default to `"local"` (project-local findings queue). Present the opt-in prompt:
50
-
51
- > Send anonymized quality findings to the shared improvement queue? [y/N]
52
-
53
- - **y** — set `health_findings_queue` to `"improvement"`
54
- - **N** (default) — keep `health_findings_queue` as `"local"`
55
-
56
- In headless mode: auto-select `"local"` (N) and log the auto-decision.
57
-
58
- ### §3 — Build State Object
59
-
60
- Construct `_campaign-state.yaml` in memory from collected inputs. Use the campaign-wide quality gate resolved in On Activation: `{qualityGateHard}` / `{qualityGateSoftTarget}` / `{qualityGateSoftFallback}`. Note: `repo_url` (collected in §1) is NOT part of the state schema — it belongs in the brief only (§5). The state schema enforces `additionalProperties: false`, so including it would fail validation.
61
-
62
- ```yaml
63
- campaign:
64
- name: "{campaign_name}"
65
- started_at: "{current_iso8601_with_tz}"
66
- last_updated: "{current_iso8601_with_tz}"
67
- current_stage: 0
68
- directive_path: "{directive_path or omit if not provided}"
69
- architecture_doc_path: "{architecture_doc_path or omit if not provided}"
70
- quality_gate:
71
- hard: "{qualityGateHard}"
72
- soft_target: {qualityGateSoftTarget}
73
- soft_fallback: {qualityGateSoftFallback}
74
- health_findings_queue: "{local or improvement}"
75
- skills:
76
- # One entry per target:
77
- - name: "{target.name}"
78
- status: "pending"
79
- depends_on: [] # from target.depends_on
80
- tier: "{target.tier}"
81
- pin: null # from target.pin
82
- brief_path: null # populated in step-05 once BS produces the skill's brief
83
- skill_path: null
84
- quality_score: null
85
- workarounds_applied: []
86
- started_at: null
87
- completed_at: null
88
- dependency_graph:
89
- execution_order: [] # populated by step-02-strategy
90
- circular_deps_detected: false
91
- ```
27
+ ### §1: Collect Inputs
28
+
29
+ A campaign needs a `campaign_name` and its target libraries. Each target has:
30
+
31
+ - `name`: skill name
32
+ - `repo_url`: source repository URL
33
+ - `tier`: `"A"` (full pipeline) or `"B"` (batch)
34
+ - `pin`: version pin (string), or `null` for latest
35
+ - `depends_on`: array of skill names this target depends on (may be empty)
36
+
37
+ Two paths are optional:
38
+
39
+ - `directive_path`: a `_campaign-directive.md` file with operator directives (contract: `references/campaign-directive-spec.md`)
40
+ - `architecture_doc_path`: the architecture document the verify (Stage 7) and refine (Stage 8) stages consume. If omitted here, those stages discover it at runtime (`{project-root}/docs/architecture.md`, then `{project-root}/_bmad-output/planning-artifacts/architecture.md`). Capturing it now persists the choice across resume and avoids re-prompting.
41
+
42
+ A field no source gives takes its default: `tier` `"A"`, `pin` `null`, `depends_on` empty, and `campaign_name` the brief's `campaign_name`, else `{project_name}`. A Tier A target may not depend on a Tier B target: Tier B skills are built after the skill loop, so Strategy rejects that plan.
43
+
44
+ Every target goes through `{manifestScript}`, whatever its source, so no target reaches state unchecked. It rejects a malformed target, a `repo_url` that is no GitHub repository (it reads a URL, an SSH URL or `owner/repo`, and writes each as `https://github.com/<owner>/<repo>`), a name that is no skill name (lower-case letters, digits and hyphens, at most 64 characters), a Tier A pin that is no X.Y.Z version (the only pin brief-skill builds from) and a duplicate name. It fills an empty name from the repository (`filled_names`), and lists each `depends_on` name that is no target (`dangling_depends_on`) and each Tier A target that depends on a Tier B target (`tier_inversions`), each entry naming its `skill` and `depends_on`. The state and the brief take their targets from the script's `targets[]`, never from the raw source.
45
+
46
+ **Manifest format** (a `--manifest` file, and a pasted list rewritten for the script): one `name,repo_url,tier,pin` target per line, where an empty `name` takes the repository's name and an empty `pin` means the latest release; a trailing `;dep1,dep2` segment sets `depends_on`; blank and `#` lines are skipped.
92
47
 
93
- ### §4 — Write + Validate State
48
+ **Headless** (`--brief` and `--manifest` imply it): take the inputs from the source On Activation parsed. Parse a `--manifest` file with `uv run {manifestScript} <manifest-file>` and a `--brief` file with `uv run {manifestScript} --brief <brief-file>`, which also returns the brief's `campaign_name`, `quality_gate`, `architecture_doc_path` and `notes` under `brief`. Headless asks nothing about `dangling_depends_on` or `tier_inversions`: Strategy halts on them (exit code 4).
94
49
 
95
- 1. Ensure the directory `{campaignWorkspacePath}/` exists (create if missing).
96
- 2. Write the constructed state to `{stateFile}`. This is the initial creation — no `.bak` is needed for the first write; all subsequent steps use read-backup-modify-write.
97
- 3. Run `uv run {validateScript} --state-file {stateFile}`. On non-zero (invalid), **HALT** (exit 3) with the script's `errors[]` — do not proceed to brief generation with an invalid state file.
50
+ **Interactive:** open with this one question, and ask nothing before it:
51
+
52
+ > Share the targets for this campaign in whatever form you have them: a manifest or `campaign-brief.yaml` path, a pasted list, or repository URLs. Add a campaign name and a directive or architecture document path if you have them.
53
+
54
+ Turn the answer into targets: a manifest path goes through `uv run {manifestScript} <manifest-file>`, and a brief path through `uv run {manifestScript} --brief <brief-file>`. A pasted list or bare repository URLs are first rewritten as manifest lines in the manifest format above (leave the name empty when none is given: the script takes it from the repository), then piped to `uv run {manifestScript} -`. Show the drafted campaign once for correction: the campaign name, the directive and architecture paths (or none), and one table of the targets with `name`, `repo_url`, `tier`, `pin` and `depends_on`. After the operator's corrections, ask only about what is still missing or ambiguous, all in one question, read from the script's output rather than by matching names yourself: each `dangling_depends_on` entry (a `depends_on` name that is no target), each `tier_inversions` entry (make the dependency Tier A, or drop it) and each `filled_names` entry (confirm the name). Run a corrected set through the script again before §2.
55
+
56
+ A target the script rejects (`errors[]` non-empty, exit 1: a malformed manifest line or brief target, a `repo_url` that is no GitHub repository, a name that is no skill name, a Tier A pin that is no X.Y.Z version, a duplicate name, a brief `pin` that is not a string) never yields a partial target set: in headless, HALT (exit code 2, `invalid-input`) listing each error's line or target number and message; interactively, show them and ask for those targets corrected. An exit 2 (a file that is missing, unreadable or not a YAML mapping) HALTs (exit code 2, `invalid-input`) with its `error`.
57
+
58
+ If no targets can be collected (an empty answer, or an empty `--brief` or `--manifest`), HALT (exit code 2, `invalid-input`) with guidance: a campaign needs at least one target.
59
+
60
+ ### §2: Create State
61
+
62
+ Settle the campaign name (the brief's `campaign_name`, else the operator's, else `{project_name}`), then create the state in one call. Pipe in the parse of the settled targets: the last §1 parse run again (the manifest file, `--brief <brief-file>`, or the corrected manifest lines on stdin), so the state takes its targets from the script's `targets[]`, never from the raw source:
63
+
64
+ ```
65
+ uv run {manifestScript} <the settled source> | uv run {stateScript} init --state-file {stateFile} --targets-file - --name "<campaign_name>" --hard "{qualityGateHard}" --soft-target {qualityGateSoftTarget} --soft-fallback {qualityGateSoftFallback} [--brief-file <brief-file>] [--directive-path <directive_path>] [--architecture-doc-path <architecture_doc_path>] --brief-out {briefFile} --brief-template {templateFile} [--notes '<operator notes>']
66
+ ```
98
67
 
99
- ### §5 — Generate Brief
68
+ Pass `--brief-file` when the targets came from a campaign brief: each value its `quality_gate` gives wins over the customize.toml one. The directive's `## Quality Overrides` apply later, while the campaign runs, and are never written here. The helper settles and checks the gate with `campaign-quality-gate.py check`, creates `{campaignWorkspacePath}/`, writes every target as a `pending` skill, stamps `started_at` and `last_updated` from the clock, and validates the state before it writes it. There is no earlier state, so no `.bak` is written. It then writes `{briefFile}` from `{templateFile}`: the campaign name, `created_at` (the state's `started_at`), the same `targets[]`, one entry per target with `name`, `repo_url`, `tier`, `pin` and `depends_on`, plus every other field its source gave it (a language or scope hint, for example), the settled `quality_gate`, `architecture_doc_path` and `notes`. `repo_url` lives only in the brief: the state schema has no place for it. Pass `--notes` only with notes the operator gave, single-quoted as a decision-log entry is (`references/campaign-contracts.md`); without it a brief's own `notes` carry over.
100
69
 
101
- Populate `{templateFile}` with collected inputs and write to `{briefFile}`. Fill in:
70
+ On exit 2, HALT (exit code 2, `invalid-input`) with its `error` and `errors[]`; nothing is written: a gate the gate script rejects (a hard gate other than `zero-critical-high`, a soft value outside 0 to 100, a fallback above the target, or a brief it cannot read), targets that hold errors, a brief template that is no YAML mapping, or a state file or backup that already exists. On exit 3, HALT (exit code 3, `invalid-state`) with its `errors[]`.
102
71
 
103
- - `campaign_name` — from collected input
104
- - `created_at` — current ISO-8601 timestamp with timezone
105
- - `targets` — array of target entries with `name`, `repo_url`, `tier`, `pin`, `depends_on`
106
- - `quality_gate` — `{qualityGateHard}` / `{qualityGateSoftTarget}` / `{qualityGateSoftFallback}`
107
- - `health_findings_queue` — from the §2 preference decision
108
- - `architecture_doc_path` — from collected input, or empty string if not provided
109
- - `notes` — operator-provided context, or empty string
72
+ ### §3: The Brief
110
73
 
111
- The brief is a machine-readable snapshot enabling fresh-context resume.
74
+ `init` (§2) wrote `{briefFile}` from the same `targets[]`, so write nothing by hand: it is the machine-readable snapshot a fresh session resumes from.
112
75
 
113
76
  ## OUTPUT
114
77
 
@@ -116,6 +79,5 @@ Confirm state file creation and brief generation. Display summary:
116
79
 
117
80
  - Campaign name
118
81
  - Number of targets
119
- - Health queue setting
120
82
 
121
83
  Chain to `{nextStepFile}`.
@@ -2,8 +2,9 @@
2
2
  nextStepFile: 'step-03-pins.md'
3
3
  stateSchemaFile: 'assets/campaign-state-schema.json'
4
4
  stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
5
- backupFile: '{campaignWorkspacePath}/_campaign-state.yaml.bak'
5
+ decisionLogFile: '{campaignWorkspacePath}/_campaign-decision-log.md'
6
6
  depsScript: 'scripts/campaign-deps.py'
7
+ stateScript: 'scripts/campaign-state.py'
7
8
  validateScript: 'scripts/campaign-validate-state.py'
8
9
  ---
9
10
 
@@ -17,51 +18,41 @@ Compute the execution order from dependency edges, detect circular dependencies,
17
18
 
18
19
  ## RULES
19
20
 
20
- - This step uses the **read-backup-modify-write** pattern (state file exists from step-01).
21
- - Validate state on load via `uv run {validateScript} --state-file {stateFile}`; HALT (exit 3) on non-zero.
22
- - Write `execution_order` and `circular_deps_detected` to `dependency_graph`.
23
- - Update `campaign.current_stage` to `1`.
24
- - Update `campaign.last_updated` to current ISO-8601 with timezone on every write.
25
- - If `{headless_mode}` is true, auto-proceed through confirmation gates with the default action and log each auto-decision.
21
+ - Write `campaign.current_stage` = 1 only in this stage's final state write, after every gate: step-resume resumes at `current_stage + 1`, so an early write skips unfinished work. Here that write follows the §6 plan gate, so a campaign cancelled at the plan shows the plan again when it resumes.
22
+ - Write state and decision-log entries only through `{stateScript}`, and on a non-zero exit HALT with the same code (State Contract in `references/campaign-contracts.md`). A log entry is `uv run {stateScript} log --log-file {decisionLogFile} --type <decision|auto|event> --text '<entry>'`.
23
+ - If `{headless_mode}` is true, auto-proceed through the plan gate with its default action and log the auto-decision; emit this stage's progress events, and at any HARD HALT the error envelope, per `references/campaign-contracts.md`.
26
24
 
27
25
  ## TASKS
28
26
 
29
- ### §1 — Read + Validate State
27
+ ### §1: Read + Validate State
30
28
 
31
29
  Load `{stateFile}`. Run `uv run {validateScript} --state-file {stateFile}`; on non-zero, HALT (exit 3) with the script's `errors[]`.
32
30
 
33
- ### §2 — Backup State
34
-
35
- Copy `{stateFile}` to `{backupFile}` before any modification.
36
-
37
- ### §3 — Read Directive
31
+ ### §2: Read Directive
38
32
 
39
33
  If `campaign.directive_path` is set in state, load the file at that path and apply its contents as campaign-wide context for this stage's processing, per the directive contract in `references/campaign-directive-spec.md`. If the file is not found, continue without error (directive is optional).
40
34
 
41
- ### §4 — Compute Execution Order
35
+ ### §3: Compute Execution Order
42
36
 
43
- Run the deterministic topological sort — do not hand-compute it:
37
+ Run the deterministic topological sort; do not hand-compute it:
44
38
 
45
39
  ```
46
40
  uv run {depsScript} --compute --state-file {stateFile}
47
41
  ```
48
42
 
49
- Parse the JSON output: `execution_order` (the ordered skill names — Kahn's sort with Tier A placed before Tier B within a dependency level), `circular_deps_detected` (bool), `cycle_participants` (the unplaced skills when a cycle exists, else null), and `tier_counts` (`{"A": n, "B": m}`, for the §7 strategy view). Script exit 1 signals an unorderable graph — a cycle or a dangling `depends_on` reference — handled at §5. Script exit 2 signals the helper could not read/parse the state file; HALT (exit code 2, `invalid-input`) surfacing its error.
43
+ Parse the JSON output: `execution_order` (the ordered skill names: Kahn's sort with Tier A placed before Tier B within a dependency level), `circular_deps_detected` (bool), `cycle_participants` (the unplaced skills when a cycle exists, else null), `tier_counts` (`{"A": n, "B": m}`, for the §5 strategy view) and `tier_inversions` (each Tier A `skill` that `depends_on` a Tier B skill). Script exit 1 signals a plan the stages cannot follow (a cycle, a dangling `depends_on` reference or a tier inversion), handled at §4 before the §5 view shows any order. Script exit 2 signals the helper could not read/parse the state file; HALT (exit code 2, `invalid-input`) surfacing its error.
50
44
 
51
- ### §5 — Handle Unorderable Graph
45
+ ### §4: Handle Unorderable Graph
52
46
 
53
- If the graph cannot be ordered (script exit 1), HALT (exit code 4, `circular-deps`) — the execution order is impossible, so do not proceed. Two cases:
47
+ If the graph cannot be followed (script exit 1), HALT (exit code 4, `circular-deps`): the execution order is impossible, so do not proceed. Three cases:
54
48
 
55
49
  - **Cycle** (`circular_deps_detected: true`): list `cycle_participants` and their mutual `depends_on` edges.
56
50
  - **Dangling reference** (a `DANGLING_DEPENDENCY` error with no `execution_order`): name the skill and the unknown dependency it references.
51
+ - **Tier A on Tier B** (`tier_inversions` non-empty, a `TIER_INVERSION` error): name each `skill` and the Tier B skill it `depends_on`. Tier B skills are built in the batch stage, after the skill loop, so the Tier A skill could never pass its dependency gate. Guidance: make the dependency Tier A, or drop the dependency, then re-run `campaign` and choose overwrite.
57
52
 
58
- ### §6 — Write State
59
-
60
- Set `dependency_graph.execution_order` to the computed order. Set `dependency_graph.circular_deps_detected` to the detection result. Set `campaign.current_stage` to `1`. Set `campaign.last_updated` to current ISO-8601 with timezone. Write to `{stateFile}`.
53
+ ### §5: Present Strategy View
61
54
 
62
- ### §7 — Present Strategy View
63
-
64
- Display a human-readable strategy summary to the operator (not written to a file — display only):
55
+ Display a human-readable strategy summary to the operator (display only: it is not written to a file):
65
56
 
66
57
  ```
67
58
  CAMPAIGN STRATEGY: {campaign_name}
@@ -86,17 +77,27 @@ TIER DISTRIBUTION:
86
77
  Tier B (QS batch): {count}
87
78
  ```
88
79
 
89
- Fill the TIER DISTRIBUTION counts from `tier_counts` in the §4 script output — do not re-tally `skills[]` by hand.
80
+ Fill the TIER DISTRIBUTION counts from `tier_counts` in the §3 script output; do not re-tally `skills[]` by hand.
90
81
 
91
- ### §8 — Plan Confirmation Gate
82
+ ### §6: Plan Confirmation Gate
92
83
 
93
84
  The strategy view is the last review surface before a potentially long, mostly-unattended run begins. Present a confirmation gate:
94
85
 
95
- - `[P]roceed` — begin execution (chain to `{nextStepFile}`).
96
- - `[C]ancel` — stop now with exit code 12 (`user-cancelled`); state is intact and resumable. To change targets, edit the campaign brief (`{campaignWorkspacePath}/campaign-brief.yaml`) or re-run `campaign` (overwrite), then start again.
86
+ - `[P]roceed`: write the plan (§7), then chain to `{nextStepFile}`.
87
+ - `[C]ancel`: log the cancel, then stop with exit code 12 (`user-cancelled`). Nothing of this stage is written, so the state is intact and a resume shows this plan again. To change targets, tiers, pins or dependencies, re-run `campaign` and choose overwrite: they live in the state that Setup wrote, and editing `campaign-brief.yaml` does not reach them.
88
+
89
+ **HALT and wait for operator input.** **GATE [default: P]**: in headless mode, auto-proceed with `[P]` and log "headless: auto-proceed past plan-confirmation gate" (type `auto`).
90
+
91
+ ### §7: Write State
92
+
93
+ On `[P]roceed`, write the plan and the stage in one write:
94
+
95
+ ```
96
+ uv run {stateScript} apply-plan --state-file {stateFile} --stage 1
97
+ ```
97
98
 
98
- **HALT and wait for operator input.** In headless mode, auto-proceed with `[P]` and log "headless: auto-proceed past plan-confirmation gate" to the decision log.
99
+ It sets `dependency_graph.execution_order` and `circular_deps_detected` from the same `campaign-deps.py --compute` the §3 view came from, so the order on disk is the order shown. On exit 4 (the graph can no longer be followed: the state changed since §3), HALT (exit code 4, `circular-deps`) with its `errors[]`; on exit 3, HALT (exit code 3, `invalid-state`).
99
100
 
100
101
  ## OUTPUT
101
102
 
102
- Confirm strategy computed, display the strategy view, and resolve the §8 gate. Chain to `{nextStepFile}`.
103
+ Confirm strategy computed, display the strategy view, resolve the §6 gate and write the §7 plan. Chain to `{nextStepFile}`.
@@ -2,9 +2,11 @@
2
2
  nextStepFile: 'step-04-provenance.md'
3
3
  stateSchemaFile: 'assets/campaign-state-schema.json'
4
4
  stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
5
- backupFile: '{campaignWorkspacePath}/_campaign-state.yaml.bak'
6
5
  briefFile: '{campaignWorkspacePath}/campaign-brief.yaml'
6
+ pinResultsFile: '{campaignWorkspacePath}/_pin-results.json'
7
+ decisionLogFile: '{campaignWorkspacePath}/_campaign-decision-log.md'
7
8
  pinScript: 'scripts/campaign-validate-pins.py'
9
+ stateScript: 'scripts/campaign-state.py'
8
10
  validateScript: 'scripts/campaign-validate-state.py'
9
11
  ---
10
12
 
@@ -18,39 +20,45 @@ Validate all version pins against real releases/branches before the campaign pro
18
20
 
19
21
  ## RULES
20
22
 
21
- - This step uses the **read-backup-modify-write** pattern (state file exists from step-01).
22
- - Validate state on load via `uv run {validateScript} --state-file {stateFile}`; HALT (exit 3) on non-zero.
23
- - Update `campaign.current_stage` to `2`.
24
- - Update `campaign.last_updated` to current ISO-8601 with timezone on every write.
25
- - HALT (exit code 5, `invalid-pin`) on any invalid pin — invalid pins are errors, not gates.
26
- - If `{headless_mode}` is true, auto-proceed through confirmation gates with the default action and log each auto-decision.
23
+ - Write `campaign.current_stage` = 2 only in this stage's final state write, after every gate: step-resume resumes at `current_stage + 1`, so an early write skips unfinished work.
24
+ - Write state and decision-log entries only through `{stateScript}`, and on a non-zero exit HALT with the same code (State Contract in `references/campaign-contracts.md`). A log entry is `uv run {stateScript} log --log-file {decisionLogFile} --type <decision|auto|event> --text '<entry>'`.
25
+ - HALT (exit code 5, `invalid-pin`) on any invalid pin: invalid pins are errors, not gates.
26
+ - If `{headless_mode}` is true, emit this stage's progress events, and at any HARD HALT the error envelope, per `references/campaign-contracts.md`.
27
27
 
28
28
  ## TASKS
29
29
 
30
- ### §1 — Read + Validate State
30
+ ### §1: Read + Validate State
31
31
 
32
32
  Load `{stateFile}`. Run `uv run {validateScript} --state-file {stateFile}`; on non-zero, HALT (exit 3) with the script's `errors[]`.
33
33
 
34
- ### §2 — Read Brief
34
+ ### §2: Read Brief
35
35
 
36
- Load `{briefFile}`. Build a lookup map from `targets[].name` to `targets[].repo_url`. HALT (exit code 8, `missing-brief`) if the brief is missing or unreadable.
36
+ Load `{briefFile}` only to confirm it parses (the pin script reads it directly). HALT (exit code 8, `missing-brief`) if the brief is missing or unreadable.
37
37
 
38
- ### §3 — Backup State
38
+ ### §3: Validate Pins
39
39
 
40
- Copy `{stateFile}` to `{backupFile}` before any modification.
40
+ Run the pin script, keeping its JSON output in `{pinResultsFile}` for §5:
41
41
 
42
- ### §4 — Validate Pins
42
+ ```
43
+ uv run {pinScript} --state-file {stateFile} --brief-file {briefFile} > {pinResultsFile}
44
+ ```
43
45
 
44
- Run `uv run {pinScript} --state-file {stateFile} --brief-file {briefFile}`. If the script exits 2 (a required tool such as `gh` is unavailable, or a file is unreadable), HALT (exit code 2, `invalid-input`) surfacing its error — pins cannot be validated without `gh`. Otherwise parse the JSON output. For each result: if `status` is `"valid"` or `"resolved"`, the pin is good; if `"invalid"`, collect the failure with suggestions.
46
+ If the script exits 2 (a required tool such as `gh` is unavailable, a file is unreadable, or `INVALID_BRIEF`: a brief that is no mapping or holds a target with no `name` or no `repo_url`), HALT (exit code 2, `invalid-input`) surfacing its error; only when `gh` is what is missing, add that pins cannot be validated without it. Otherwise read `{pinResultsFile}`. For each result: if `status` is `"valid"` or `"resolved"`, the pin is good; if `"invalid"`, collect the failure with suggestions.
45
47
 
46
- ### §5 — Handle Invalid Pins
48
+ ### §4: Handle Invalid Pins
47
49
 
48
50
  If ANY pins are invalid, collect ALL failures first (all-or-nothing pattern), then HALT (exit code 5, `invalid-pin`) with a clear error listing each invalid pin, the skill name, the attempted pin value, and suggested corrections. Do NOT partially proceed.
49
51
 
50
- ### §6 — Update State
52
+ ### §5: Write State
51
53
 
52
- For each skill where validation returned `status: "resolved"` (pin was null, latest release found), update `skill.pin` to the `resolved_ref` value. For `status: "valid"` where the input pin differs from `resolved_ref` (e.g., user said `2.0.0` but the actual tag is `v2.0.0`), update `skill.pin` to the `resolved_ref` so downstream steps use the exact ref name. Set `campaign.current_stage` to `2`. Set `campaign.last_updated`. Write to `{stateFile}`.
54
+ Write every resolved pin and the stage in one write:
55
+
56
+ ```
57
+ uv run {stateScript} apply-pins --state-file {stateFile} --results-file {pinResultsFile} --stage 2
58
+ ```
59
+
60
+ It sets each skill's `pin` to the script's `resolved_ref` wherever the two differ: a null pin that resolved to the latest release, or a pin written without its tag's form (`2.0.0` for the tag `v2.0.0`), so downstream steps use the exact ref name. It refuses results that hold an invalid pin (exit 5, nothing written: HALT as in §4); on exit 3, HALT (exit code 3, `invalid-state`).
53
61
 
54
62
  ## OUTPUT
55
63
 
56
- Display pin validation summary — for each skill: name, pin, resolved ref, ref type. Chain to `{nextStepFile}`.
64
+ Display pin validation summary: for each skill, name, pin, resolved ref, ref type. Chain to `{nextStepFile}`.