bmad-module-skill-forge 1.9.0 → 2.0.1

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 (281) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.nvmrc +1 -1
  3. package/README.md +10 -5
  4. package/docs/_data/pinned.yaml +2 -2
  5. package/docs/agents.md +11 -2
  6. package/docs/architecture.md +5 -4
  7. package/docs/bmad-synergy.md +30 -7
  8. package/docs/campaign.md +172 -0
  9. package/docs/examples.md +7 -3
  10. package/docs/forge-auto.md +90 -0
  11. package/docs/getting-started.md +9 -0
  12. package/docs/how-it-works.md +6 -4
  13. package/docs/index.md +4 -3
  14. package/docs/skill-model.md +1 -1
  15. package/docs/troubleshooting.md +17 -1
  16. package/docs/verifying-a-skill.md +2 -2
  17. package/docs/why-skf.md +1 -1
  18. package/docs/workflows.md +64 -24
  19. package/package.json +2 -2
  20. package/src/knowledge/skill-lifecycle.md +1 -1
  21. package/src/module-help.csv +4 -3
  22. package/src/shared/data/language-corpora.json +32 -0
  23. package/src/shared/references/pipeline-contracts.md +6 -3
  24. package/src/shared/scripts/schemas/skill-brief.v1.json +6 -1
  25. package/src/shared/scripts/skf-comention-pairs.py +350 -0
  26. package/src/shared/scripts/skf-count-tokens.py +222 -0
  27. package/src/shared/scripts/skf-derive-assembly-shape.py +107 -0
  28. package/src/shared/scripts/skf-detect-docs.py +240 -4
  29. package/src/shared/scripts/skf-detect-language.py +82 -3
  30. package/src/shared/scripts/skf-detect-tools.py +105 -2
  31. package/src/shared/scripts/skf-emit-result-envelope.py +17 -0
  32. package/src/shared/scripts/skf-enumerate-stack-skills.py +111 -2
  33. package/src/shared/scripts/skf-extract-public-api.py +53 -0
  34. package/src/shared/scripts/skf-find-cycles.py +246 -0
  35. package/src/shared/scripts/skf-hash-content.py +487 -2
  36. package/src/shared/scripts/skf-language-corpora.py +100 -0
  37. package/src/shared/scripts/skf-manifest-ops.py +67 -1
  38. package/src/shared/scripts/skf-merge-doc-urls.py +244 -0
  39. package/src/shared/scripts/skf-preapply.py +1 -1
  40. package/src/shared/scripts/skf-rebuild-managed-sections.py +180 -0
  41. package/src/shared/scripts/skf-recommend-scope-type.py +1 -2
  42. package/src/shared/scripts/skf-render-metadata-stats.py +499 -0
  43. package/src/shared/scripts/skf-rewrite-skill-name.py +308 -0
  44. package/src/shared/scripts/skf-shape-detect.py +576 -25
  45. package/src/shared/scripts/skf-shard-body.py +461 -0
  46. package/src/shared/scripts/skf-skill-inventory.py +139 -3
  47. package/src/shared/scripts/skf-structural-diff.py +307 -67
  48. package/src/shared/scripts/skf-validate-brief-schema.py +41 -6
  49. package/src/shared/scripts/skf-validate-feasibility-report.py +234 -0
  50. package/src/shared/scripts/skf-validate-frontmatter.py +17 -12
  51. package/src/shared/scripts/skf-validate-output.py +443 -10
  52. package/src/shared/scripts/skf-validate-pins.py +3 -3
  53. package/src/shared/scripts/skf-verify-no-trace.py +214 -0
  54. package/src/shared/scripts/skf-verify-provenance-completeness.py +439 -0
  55. package/src/shared/scripts/skf-write-skill-brief.py +21 -5
  56. package/src/skf-analyze-source/SKILL.md +17 -32
  57. package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
  58. package/src/skf-analyze-source/customize.toml +2 -0
  59. package/src/skf-analyze-source/references/auto-docs-only.md +116 -0
  60. package/src/skf-analyze-source/references/continue.md +8 -13
  61. package/src/skf-analyze-source/references/generate-briefs.md +28 -12
  62. package/src/skf-analyze-source/references/headless-contract.md +33 -0
  63. package/src/skf-analyze-source/references/health-check.md +1 -2
  64. package/src/skf-analyze-source/references/identify-units.md +25 -36
  65. package/src/skf-analyze-source/references/init.md +12 -25
  66. package/src/skf-analyze-source/references/map-and-detect.md +8 -20
  67. package/src/skf-analyze-source/references/recommend.md +4 -14
  68. package/src/skf-analyze-source/references/scan-project.md +3 -11
  69. package/src/skf-analyze-source/references/step-auto-scope.md +211 -197
  70. package/src/skf-analyze-source/references/step-shape-detect.md +20 -13
  71. package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -2
  72. package/src/skf-audit-skill/SKILL.md +9 -6
  73. package/src/skf-audit-skill/customize.toml +2 -0
  74. package/src/skf-audit-skill/references/init.md +58 -32
  75. package/src/skf-audit-skill/references/re-index.md +15 -34
  76. package/src/skf-audit-skill/references/report.md +11 -58
  77. package/src/skf-audit-skill/references/semantic-diff.md +4 -22
  78. package/src/skf-audit-skill/references/severity-classify.md +53 -87
  79. package/src/skf-audit-skill/references/step-doc-drift.md +49 -19
  80. package/src/skf-audit-skill/references/structural-diff.md +43 -68
  81. package/src/skf-brief-skill/SKILL.md +7 -36
  82. package/src/skf-brief-skill/assets/scope-templates.md +1 -1
  83. package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -1
  84. package/src/skf-brief-skill/customize.toml +14 -0
  85. package/src/skf-brief-skill/references/analyze-target.md +13 -18
  86. package/src/skf-brief-skill/references/confirm-brief.md +2 -15
  87. package/src/skf-brief-skill/references/draft-checkpoint.md +8 -8
  88. package/src/skf-brief-skill/references/gather-intent.md +27 -24
  89. package/src/skf-brief-skill/references/headless-args.md +1 -1
  90. package/src/skf-brief-skill/references/health-check.md +1 -22
  91. package/src/skf-brief-skill/references/invocation-contract.md +37 -0
  92. package/src/skf-brief-skill/references/scope-definition.md +6 -15
  93. package/src/skf-brief-skill/references/step-auto-brief.md +22 -9
  94. package/src/skf-brief-skill/references/step-auto-validate.md +3 -7
  95. package/src/skf-brief-skill/references/version-resolution.md +1 -1
  96. package/src/skf-brief-skill/references/write-brief.md +19 -13
  97. package/src/skf-campaign/SKILL.md +121 -0
  98. package/src/skf-campaign/assets/campaign-state-schema.json +191 -0
  99. package/src/skf-campaign/customize.toml +75 -0
  100. package/src/skf-campaign/manifest.yaml +15 -0
  101. package/src/skf-campaign/references/campaign-contracts.md +67 -0
  102. package/src/skf-campaign/references/campaign-directive-spec.md +32 -0
  103. package/src/skf-campaign/references/health-check.md +35 -0
  104. package/src/skf-campaign/references/step-01-setup.md +121 -0
  105. package/src/skf-campaign/references/step-02-strategy.md +102 -0
  106. package/src/skf-campaign/references/step-03-pins.md +56 -0
  107. package/src/skf-campaign/references/step-04-provenance.md +63 -0
  108. package/src/skf-campaign/references/step-05-skill-loop.md +103 -0
  109. package/src/skf-campaign/references/step-06-batch.md +88 -0
  110. package/src/skf-campaign/references/step-07-capstone.md +63 -0
  111. package/src/skf-campaign/references/step-08-verify.md +75 -0
  112. package/src/skf-campaign/references/step-09-refine.md +83 -0
  113. package/src/skf-campaign/references/step-10-export.md +106 -0
  114. package/src/skf-campaign/references/step-11-maintenance.md +84 -0
  115. package/src/skf-campaign/references/step-resume.md +115 -0
  116. package/src/skf-campaign/scripts/.gitkeep +0 -0
  117. package/src/skf-campaign/scripts/campaign-deps.py +244 -0
  118. package/src/skf-campaign/scripts/campaign-parse-manifest.py +119 -0
  119. package/src/skf-campaign/scripts/campaign-provenance.py +247 -0
  120. package/src/skf-campaign/scripts/campaign-render-batch.py +199 -0
  121. package/src/skf-campaign/scripts/campaign-render-kickoff.py +158 -0
  122. package/src/skf-campaign/scripts/campaign-report.py +249 -0
  123. package/src/skf-campaign/scripts/campaign-status.py +162 -0
  124. package/src/skf-campaign/scripts/campaign-validate-pins.py +174 -0
  125. package/src/skf-campaign/scripts/campaign-validate-state.py +207 -0
  126. package/src/skf-campaign/templates/campaign-brief-template.yaml +34 -0
  127. package/src/skf-campaign/templates/campaign-report-template.md +54 -0
  128. package/src/skf-campaign/templates/kickoff-template.md +48 -0
  129. package/src/skf-create-skill/SKILL.md +15 -4
  130. package/src/skf-create-skill/assets/compile-assembly-rules.md +35 -34
  131. package/src/skf-create-skill/assets/skill-sections.md +9 -100
  132. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +4 -13
  133. package/src/skf-create-skill/customize.toml +13 -0
  134. package/src/skf-create-skill/references/authoritative-files-protocol.md +7 -7
  135. package/src/skf-create-skill/references/compile.md +37 -47
  136. package/src/skf-create-skill/references/component-extraction.md +8 -35
  137. package/src/skf-create-skill/references/ecosystem-check.md +9 -25
  138. package/src/skf-create-skill/references/enrich.md +4 -17
  139. package/src/skf-create-skill/references/extract.md +20 -27
  140. package/src/skf-create-skill/references/extraction-patterns-tracing.md +1 -1
  141. package/src/skf-create-skill/references/extraction-patterns.md +10 -8
  142. package/src/skf-create-skill/references/generate-artifacts.md +20 -25
  143. package/src/skf-create-skill/references/health-check.md +2 -2
  144. package/src/skf-create-skill/references/load-brief.md +16 -18
  145. package/src/skf-create-skill/references/report.md +27 -14
  146. package/src/skf-create-skill/references/source-resolution-protocols.md +4 -4
  147. package/src/skf-create-skill/references/step-auto-shard.md +52 -25
  148. package/src/skf-create-skill/references/step-doc-rot.md +33 -15
  149. package/src/skf-create-skill/references/step-doc-sources.md +13 -4
  150. package/src/skf-create-skill/references/sub/ccc-discover.md +2 -13
  151. package/src/skf-create-skill/references/sub/fetch-docs.md +33 -46
  152. package/src/skf-create-skill/references/sub/fetch-temporal.md +5 -20
  153. package/src/skf-create-skill/references/tier-degradation-rules.md +6 -8
  154. package/src/skf-create-skill/references/validate.md +69 -29
  155. package/src/skf-create-skill/scripts/scan-doc-rot.py +245 -0
  156. package/src/skf-create-stack-skill/SKILL.md +15 -9
  157. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +1 -1
  158. package/src/skf-create-stack-skill/customize.toml +10 -0
  159. package/src/skf-create-stack-skill/references/compile-stack.md +7 -8
  160. package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -13
  161. package/src/skf-create-stack-skill/references/detect-integrations.md +49 -54
  162. package/src/skf-create-stack-skill/references/detect-manifests.md +37 -30
  163. package/src/skf-create-stack-skill/references/generate-output.md +24 -75
  164. package/src/skf-create-stack-skill/references/init.md +9 -81
  165. package/src/skf-create-stack-skill/references/manifest-patterns.md +5 -35
  166. package/src/skf-create-stack-skill/references/parallel-extract.md +12 -27
  167. package/src/skf-create-stack-skill/references/rank-and-confirm.md +6 -6
  168. package/src/skf-create-stack-skill/references/report.md +27 -62
  169. package/src/skf-create-stack-skill/references/validate.md +35 -31
  170. package/src/skf-drop-skill/SKILL.md +20 -23
  171. package/src/skf-drop-skill/customize.toml +10 -0
  172. package/src/skf-drop-skill/references/execute.md +67 -36
  173. package/src/skf-drop-skill/references/headless-contract.md +18 -0
  174. package/src/skf-drop-skill/references/health-check.md +1 -13
  175. package/src/skf-drop-skill/references/report.md +44 -10
  176. package/src/skf-drop-skill/references/select.md +93 -48
  177. package/src/skf-drop-skill/scripts/dir-sizes.py +116 -0
  178. package/src/skf-export-skill/SKILL.md +17 -14
  179. package/src/skf-export-skill/assets/managed-section-format.md +2 -5
  180. package/src/skf-export-skill/customize.toml +10 -6
  181. package/src/skf-export-skill/references/generate-snippet.md +21 -35
  182. package/src/skf-export-skill/references/load-skill.md +44 -42
  183. package/src/skf-export-skill/references/multi-skill-mode.md +1 -1
  184. package/src/skf-export-skill/references/orphan-context-detection.md +11 -3
  185. package/src/skf-export-skill/references/package.md +27 -33
  186. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +24 -5
  187. package/src/skf-export-skill/references/result-envelope.md +28 -0
  188. package/src/skf-export-skill/references/summary.md +11 -5
  189. package/src/skf-export-skill/references/token-report.md +42 -24
  190. package/src/skf-export-skill/references/update-context.md +32 -33
  191. package/src/skf-forger/SKILL.md +32 -39
  192. package/src/skf-forger/references/pipeline-mode.md +34 -0
  193. package/src/skf-forger/scripts/parse-pipeline.py +270 -0
  194. package/src/skf-quick-skill/SKILL.md +10 -60
  195. package/src/skf-quick-skill/customize.toml +9 -0
  196. package/src/skf-quick-skill/references/batch-mode.md +6 -4
  197. package/src/skf-quick-skill/references/compile.md +15 -52
  198. package/src/skf-quick-skill/references/ecosystem-check.md +5 -20
  199. package/src/skf-quick-skill/references/finalize.md +12 -6
  200. package/src/skf-quick-skill/references/halt-contract.md +54 -0
  201. package/src/skf-quick-skill/references/health-check.md +3 -4
  202. package/src/skf-quick-skill/references/quick-extract.md +7 -18
  203. package/src/skf-quick-skill/references/registry-resolution.md +1 -23
  204. package/src/skf-quick-skill/references/resolve-target.md +27 -34
  205. package/src/skf-quick-skill/references/write-and-validate.md +10 -25
  206. package/src/skf-refine-architecture/SKILL.md +16 -8
  207. package/src/skf-refine-architecture/customize.toml +9 -0
  208. package/src/skf-refine-architecture/references/compile.md +10 -8
  209. package/src/skf-refine-architecture/references/gap-analysis.md +21 -50
  210. package/src/skf-refine-architecture/references/health-check.md +1 -1
  211. package/src/skf-refine-architecture/references/improvements.md +9 -25
  212. package/src/skf-refine-architecture/references/init.md +9 -16
  213. package/src/skf-refine-architecture/references/issue-detection.md +10 -25
  214. package/src/skf-refine-architecture/references/refinement-rules.md +4 -16
  215. package/src/skf-refine-architecture/references/report.md +14 -11
  216. package/src/skf-rename-skill/SKILL.md +11 -21
  217. package/src/skf-rename-skill/customize.toml +10 -0
  218. package/src/skf-rename-skill/references/execute.md +112 -82
  219. package/src/skf-rename-skill/references/exit-codes.md +12 -0
  220. package/src/skf-rename-skill/references/report.md +16 -8
  221. package/src/skf-rename-skill/references/select.md +69 -42
  222. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -0
  223. package/src/skf-setup/SKILL.md +21 -13
  224. package/src/skf-setup/customize.toml +10 -0
  225. package/src/skf-setup/references/auto-index.md +11 -10
  226. package/src/skf-setup/references/ccc-index.md +14 -15
  227. package/src/skf-setup/references/detect-and-tier.md +10 -8
  228. package/src/skf-setup/references/report.md +26 -32
  229. package/src/skf-setup/references/tier-rules.md +2 -32
  230. package/src/skf-setup/references/write-config.md +9 -10
  231. package/src/skf-test-skill/SKILL.md +10 -8
  232. package/src/skf-test-skill/customize.toml +2 -0
  233. package/src/skf-test-skill/references/coherence-check.md +19 -31
  234. package/src/skf-test-skill/references/coverage-check.md +111 -67
  235. package/src/skf-test-skill/references/detect-mode.md +10 -41
  236. package/src/skf-test-skill/references/external-validators.md +11 -19
  237. package/src/skf-test-skill/references/init.md +39 -21
  238. package/src/skf-test-skill/references/migration-section-rules.md +4 -4
  239. package/src/skf-test-skill/references/report.md +60 -40
  240. package/src/skf-test-skill/references/score.md +32 -64
  241. package/src/skf-test-skill/references/scoring-rules.md +6 -62
  242. package/src/skf-test-skill/references/source-access-protocol.md +15 -5
  243. package/src/skf-test-skill/references/step-hard-gate.md +1 -3
  244. package/src/skf-test-skill/scripts/aggregate-coherence.py +276 -0
  245. package/src/skf-test-skill/scripts/check-metadata-coherence.py +359 -0
  246. package/src/skf-test-skill/scripts/combine-external-scores.py +190 -0
  247. package/src/skf-test-skill/scripts/compute-score.py +135 -4
  248. package/src/skf-test-skill/scripts/reconcile-coverage.py +327 -0
  249. package/src/skf-test-skill/scripts/validate-inventory.py +251 -0
  250. package/src/skf-test-skill/scripts/verify-declared-numerator.py +192 -0
  251. package/src/skf-update-skill/SKILL.md +17 -5
  252. package/src/skf-update-skill/customize.toml +11 -6
  253. package/src/skf-update-skill/references/detect-changes.md +48 -36
  254. package/src/skf-update-skill/references/health-check.md +5 -11
  255. package/src/skf-update-skill/references/init.md +40 -32
  256. package/src/skf-update-skill/references/manual-section-rules.md +1 -2
  257. package/src/skf-update-skill/references/merge-conflict-rules.md +1 -52
  258. package/src/skf-update-skill/references/merge.md +13 -57
  259. package/src/skf-update-skill/references/re-extract.md +27 -25
  260. package/src/skf-update-skill/references/report.md +14 -7
  261. package/src/skf-update-skill/references/validate.md +32 -30
  262. package/src/skf-update-skill/references/write.md +86 -51
  263. package/src/skf-update-skill/scripts/skf-new-file-diff.py +167 -0
  264. package/src/skf-verify-stack/SKILL.md +11 -21
  265. package/src/skf-verify-stack/customize.toml +12 -3
  266. package/src/skf-verify-stack/references/coverage-patterns.md +1 -29
  267. package/src/skf-verify-stack/references/coverage.md +21 -10
  268. package/src/skf-verify-stack/references/exit-codes.md +14 -0
  269. package/src/skf-verify-stack/references/health-check.md +1 -1
  270. package/src/skf-verify-stack/references/init.md +20 -25
  271. package/src/skf-verify-stack/references/integration-verification-rules.md +10 -19
  272. package/src/skf-verify-stack/references/integrations.md +44 -49
  273. package/src/skf-verify-stack/references/report.md +28 -34
  274. package/src/skf-verify-stack/references/requirements.md +3 -3
  275. package/src/skf-verify-stack/references/synthesize.md +36 -39
  276. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +196 -0
  277. package/src/skf-verify-stack/scripts/skf-report-delta.py +256 -0
  278. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +258 -0
  279. package/tools/cli/lib/ui.js +3 -2
  280. package/docs/deepwiki.md +0 -89
  281. package/src/skf-rename-skill/references/rebuild-context.md +0 -110
@@ -1,5 +1,4 @@
1
1
  ---
2
- briefSchemaFile: 'assets/skill-brief-schema.md'
3
2
  versionResolutionFile: 'references/version-resolution.md'
4
3
  qmdRegistrationFile: 'references/qmd-collection-registration.md'
5
4
  nextStepFile: 'health-check.md'
@@ -19,20 +18,15 @@ forgeTierFile: '{sidecar_path}/forge-tier.yaml'
19
18
 
20
19
  # Step 5: Write Brief
21
20
 
22
- ## STEP GOAL:
23
-
24
- To generate the complete skill-brief.yaml from the approved brief data and write it to the forge data folder, completing the brief-skill workflow.
25
-
26
21
  ## Rules
27
22
 
28
23
  - Focus only on writing the file — all decisions have been made
29
24
  - Do not change any field values without user request — the brief was already approved
30
- - Create the output directory if it doesn't exist
31
25
  - Chains to the local health-check step via `{nextStepFile}` after completion — the user-facing success summary is NOT the terminal step
32
26
  - All user-facing output in `{communication_language}`; written artifact (`description`, `notes`) in `{document_output_language}`
33
27
  - **Determinism delegation:** YAML rendering, version-precedence, atomic write, the headless result envelope, and the QMD-collection registry mutation are all delegated to shared SKF scripts. The LLM's job in this step is to assemble inputs, branch on script results, and surface user-facing prose — not to render YAML, JSON envelopes, or YAML-mutation diffs in the model.
34
28
 
35
- ## MANDATORY SEQUENCE
29
+ ## Sequence
36
30
 
37
31
  ### 1. Reference the Schema (LLM context only)
38
32
 
@@ -98,7 +92,7 @@ Assemble the brief context as a **flat** JSON object — every approved value is
98
92
  "scope_rationale": null | {"recommended":"...","chosen":"...","accepted_recommendation":true|false,"heuristic":"...","reason":"...","recorded":"YYYY-MM-DD"},
99
93
  "scope_tier_a_include": null | ["{tier-A authoring-surface patterns — from step 03 §3c capture, or hydrated on a ratify run}"],
100
94
  "scope_amendments": null | [{"path":"...","action":"...","reason":"...","date":"YYYY-MM-DD","workflow":"..."}],
101
- "doc_urls": null | [{"url": "...", "label": "..."}],
95
+ "doc_urls": null | [{"url": "...", "label": "...", "source": "{optional: language-registry|readme-detection|homepage|pages-api|docs-folder}"}],
102
96
  "scripts_intent": null | "{detect|none|free-text}",
103
97
  "assets_intent": null | "{detect|none|free-text}",
104
98
  "source_authority": null | "{official|community|internal}",
@@ -163,7 +157,7 @@ The script derives `exit_code` deterministically from `halt_reason` (null→0, i
163
157
 
164
158
  The script enforces the success/error halt_reason invariant (success requires null halt_reason; error requires non-null). The `user-cancelled` halt_reason is accepted for completeness (interactive `[X]` Cancel sites in step 1/03/04) but never appears on the headless code path.
165
159
 
166
- Invocation sites (each pointed at this block, not duplicated): step 1 §1 (write-failed pre-resolution), step 1 §8 (input-missing/input-invalid GATE), step 5 §2b (overwrite-cancelled), step 5 §3 (input-invalid/write-failed from script). Step-01 §1 forge-tier-missing and step 2 §1 (target-inaccessible/gh-auth-failed) currently HALT without emitting an envelope — pre-existing gaps tracked separately, not addressed by this canonicalization.
160
+ Invocation sites (each pointed at this block, not duplicated): step 1 §1 (write-failed pre-resolution; forge-tier-missing), step 1 §8 (input-missing/input-invalid GATE), step 2 §1 (target-inaccessible/gh-auth-failed), step 5 §2b (overwrite-cancelled), step 5 §3 (input-invalid/write-failed from script). The step 1 §1 forge-tier-missing and step 2 §1 target-inaccessible/gh-auth-failed sites emit through this block too, so every headless HALT class surfaces a `SKF_BRIEF_RESULT_JSON` envelope — there are no envelope-silent failure classes.
167
161
 
168
162
  When `{headless_mode}` is false, skip this section silently — no envelope is emitted.
169
163
 
@@ -203,10 +197,22 @@ After compilation, you can:
203
197
 
204
198
  **Brief-skill workflow complete.**"
205
199
 
206
- ### 7. Chain to Health Check
200
+ ### 6b. On-Complete Hook (pipeline integration)
201
+
202
+ If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it now — after the brief has been written (§3) and the result contract finalized (§4b) — as:
203
+
204
+ ```bash
205
+ {onCompleteCommand} --result-path={brief_path}
206
+ ```
207
+
208
+ where `{brief_path}` is the absolute path captured from the §3 response envelope (the freshly written `skill-brief.yaml`, the stable artifact a downstream consumer chains from).
207
209
 
208
- ONLY WHEN the brief file has been written and the success summary displayed will you then load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the summary reads as final.
210
+ - On success: log to `workflow_warnings[]` as informational only if the hook emitted stderr (`on_complete hook stderr: …`); otherwise no entry.
211
+ - On non-zero exit / process error: log to `workflow_warnings[]` (`on_complete hook failed (exit {code}): {stderr_snippet}`).
212
+ - **Never fail the workflow on hook errors** — the hook is for pipeline integration (chaining into create-skill, Slack, dashboards, CI), not for gating brief production.
209
213
 
210
- ## CRITICAL STEP COMPLETION NOTE
214
+ When `{onCompleteCommand}` is empty (bundled default), skip this section entirely — no hook is invoked.
215
+
216
+ ### 7. Chain to Health Check
211
217
 
212
- This step chains to the local health-check step (`{nextStepFile}`), which in turn delegates to `shared/health-check.md`. After the health check completes, the brief-skill workflow is fully done.
218
+ Once the brief file has been written and the success summary displayed, load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the summary reads as final.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: skf-campaign
3
+ description: Campaign orchestration — multi-library skill production with dependency tracking, file-based state, and resume. Use when the user asks to "run a campaign" or "orchestrate skills."
4
+ ---
5
+
6
+ # Campaign
7
+
8
+ ## Overview
9
+
10
+ Orchestrates the production of 15+ skills across multiple sessions by driving them through the full SKF pipeline (brief, generate, compile, test, export) in dependency order. Campaign sits atop the pipeline ladder: it sequences the workflows that produce skills rather than producing artifacts itself. File-based state (`_campaign-state.yaml`) survives context death, enabling resume from any point.
11
+
12
+ ## Conventions
13
+
14
+ - Bare paths (e.g. `references/step-01-setup.md`) resolve from the skill root.
15
+ - `references/` holds the stage-chained step files plus reference specs (e.g. the `_campaign-directive.md` contract at `references/campaign-directive-spec.md`); `templates/`, `scripts/`, and `assets/` hold templates, deterministic helpers, and the state schema.
16
+ - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
17
+ - `{project-root}`-prefixed paths resolve from the project working directory.
18
+ - `{skill-name}` resolves to the skill directory's basename.
19
+
20
+ ## Role
21
+
22
+ You are a campaign orchestrator operating in Ferris's Management mode. You sequence workflows, track per-skill state, enforce quality gates, and ensure every skill reaches its target tier — while the individual pipeline workflows handle the actual artifact production.
23
+
24
+ ## On Activation
25
+
26
+ Run these steps once, in order, before dispatching to Mode Routing.
27
+
28
+ 1. **Load config.** Read `{project-root}/_bmad/skf/config.yaml` and `{sidecar_path}/preferences.yaml` in one batched message (independent files). From config resolve `project_name`, `user_name`, `communication_language`, `document_output_language`, `skills_output_folder`, `forge_data_folder`, `sidecar_path`. From preferences resolve `headless_mode` (default false). If the config file is missing, fall back to `forge_data_folder = forge-data`.
29
+
30
+ 2. **Resolve `{headless_mode}`** — true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `preferences.yaml`. Default: false.
31
+
32
+ 3. **Resolve workflow customization.** Run:
33
+
34
+ ```bash
35
+ python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow
36
+ ```
37
+
38
+ The script merges three layers (scalars override, arrays append):
39
+
40
+ - `{skill-root}/customize.toml` — bundled defaults
41
+ - `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
42
+ - `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
43
+
44
+ If it fails or is missing, fall back to `{skill-root}/customize.toml` directly. Resolve each scalar now (so step files never repeat the conditional) and stash as workflow-context variables:
45
+
46
+ - `{campaignWorkspacePath}` ← `workflow.campaign_workspace_path` if non-empty, else `{forge_data_folder}/_campaign`
47
+ - `{qualityGateHard}` / `{qualityGateSoftTarget}` / `{qualityGateSoftFallback}` ← the `quality_gate_*` scalars (defaults `zero-critical-high` / `90` / `80`)
48
+ - `{reportTemplatePath}` ← `workflow.report_template_path` if non-empty, else `templates/campaign-report-template.md`
49
+ - `{kickoffTemplatePath}` ← `workflow.kickoff_template_path` if non-empty, else `templates/kickoff-template.md`
50
+ - `{briefTemplatePath}` ← `workflow.brief_template_path` if non-empty, else `templates/campaign-brief-template.yaml`
51
+ - `{onComplete}` ← `workflow.on_complete` (empty = no-op)
52
+
53
+ Load `workflow.persistent_facts` (literal sentences and `file:` references, globs expanded) and keep them in mind for the whole campaign — they are injected into every per-skill kickoff. Run any `activation_steps_prepend` before step 1 and any `activation_steps_append` after this step.
54
+
55
+ 4. **Parse CLI overrides** into the workflow context:
56
+
57
+ | Flag | Effect |
58
+ | --- | --- |
59
+ | `--headless` / `-H` | Force `{headless_mode} = true` (see step 2). |
60
+ | `--brief <file>` | Seed step-01 targets from a `campaign-brief.yaml` instead of interactive prompts. Implies `--headless`. |
61
+ | `--manifest <file>` | Seed step-01 targets from a plain-text `name,repo_url,tier,pin` manifest. Implies `--headless`. |
62
+ | `--from <skill>` | Resume override — see Mode Routing. |
63
+
64
+ If `--brief` or `--manifest` is set, force `{headless_mode} = true` (log "headless: coerced by --brief/--manifest" if it was false).
65
+
66
+ **`--manifest` format:** one `name,repo_url,tier,pin` target per line (empty `pin` = latest); a trailing `;dep1,dep2` segment sets `depends_on`; blank and `#` lines are skipped. A malformed line HALTs step-01 with the offending line numbers — never a partial target set.
67
+
68
+ 5. **Dispatch** per Mode Routing below.
69
+
70
+ ## Workflow Rules
71
+
72
+ These rules apply to every step in this workflow:
73
+
74
+ - State-first — write state to disk before chaining to the next step or workflow
75
+ - Read-backup-modify-write for all state mutations (State Contract in `references/campaign-contracts.md`)
76
+ - Validate `_campaign-state.yaml` on every load by running `uv run scripts/campaign-validate-state.py --state-file {stateFile}` and HALT (exit code 3, `invalid-state`) on non-zero — never hand-validate the schema
77
+ - Zero memory dependency — campaign state is 100% recoverable from disk; never rely on conversation context for progress tracking
78
+ - Treat a missing or unparseable `SKF_*_RESULT_JSON` envelope from any sub-skill as a sub-skill failure; never write partial state from an unparsed envelope
79
+ - Append a one-line entry to the campaign decision log (`{campaignWorkspacePath}/_campaign-decision-log.md`, append-only) at every operator or auto-decision (skip/force, overwrite, export cancel/proceed, `.bak` recovery, user-cancel) so rationale survives compaction and resume
80
+ - **Universal cancel affordance** — at any interactive gate between Setup and the Export gate, `cancel`/`exit`/`:q` triggers a HARD HALT with **exit code 12 (`user-cancelled`)**: log it and leave state intact and resumable. Exception: the Export gate's own `[C]ancel` stays exit code 11 (`export-cancelled`) — never also emit 12 there, so an automator's exit-code branch stays deterministic. These keywords count only as a response *to a prompt*; a skill or campaign named `cancel`/`exit` supplied as data is never treated as a cancel.
81
+ - Always communicate in `{communication_language}`
82
+ - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
83
+ - If `{headless_mode}` is true, emit a single-line JSON progress event to **stderr** at each step's entry, exit, and HARD HALT so schedulers stream live progress — event format in `references/campaign-contracts.md` (Headless Progress Events)
84
+
85
+ ## Stages
86
+
87
+ | # | Step | File | Auto-proceed |
88
+ |---|------|------|--------------|
89
+ | 0 | Setup | references/step-01-setup.md | Yes |
90
+ | 1 | Strategy | references/step-02-strategy.md | Yes |
91
+ | 2 | Pin Validation | references/step-03-pins.md | Yes |
92
+ | 3 | Provenance | references/step-04-provenance.md | Yes |
93
+ | 4 | Skill Loop | references/step-05-skill-loop.md | Yes |
94
+ | 5 | Tier B Batch | references/step-06-batch.md | Yes |
95
+ | 6 | Capstone | references/step-07-capstone.md | Yes |
96
+ | 7 | Verification | references/step-08-verify.md | Yes |
97
+ | 8 | Refinement | references/step-09-refine.md | Yes |
98
+ | 9 | Export | references/step-10-export.md | No (write-gate HALT) |
99
+ | 10 | Maintenance | references/step-11-maintenance.md | Yes |
100
+
101
+ **Stage numbering:** step files are 1-indexed (`step-01` … `step-11`); `campaign.current_stage` in state is 0-indexed, so step-`NN` runs stage `NN − 1` (step-01 = stage 0, step-11 = stage 10). `references/step-resume.md` §3–§4 own how a resolved stage maps back to its step file on resume (including the `current_stage + 1` advance when no skill is active).
102
+
103
+ ## Invocation Contract
104
+
105
+ | Aspect | Detail |
106
+ |--------|--------|
107
+ | **Inputs** | `campaign` to start a new campaign; `campaign resume [--from=<skill>]` to resume from last active or specified skill; `campaign status` for a read-only progress summary |
108
+ | **Outputs** | `_campaign-state.yaml` (state), `campaign-brief.yaml` (machine-generated brief), `campaign-report.md` (post-campaign summary), `_campaign-decision-log.md` (append-only rationale), `SKF_CAMPAIGN_RESULT_JSON` (headless envelope) — all under `{campaignWorkspacePath}` |
109
+
110
+ ## Contracts
111
+
112
+ Exit codes, the HARD-HALT error envelope, the read-backup-modify-write **State Contract**, the headless success envelope, and per-step progress events live in `references/campaign-contracts.md` — consult it when you HALT, mutate state, or emit headless output.
113
+
114
+ ## Mode Routing
115
+
116
+ On invocation:
117
+
118
+ 1. **`campaign resume [--from=<skill>]`** — load `references/step-resume.md` (validates state, recovers from backup, chains to the right stage). `--from=<skill>` overrides the resume point to the named skill.
119
+ 2. **`campaign`** (new, no existing state) — run from stage 0 (Setup).
120
+ 3. **`campaign`** (state exists) — detect existing `{campaignWorkspacePath}/_campaign-state.yaml` and prompt **resume** (via `references/step-resume.md`) or **overwrite**. On overwrite, first archive the existing `_campaign-state.yaml` and `campaign-brief.yaml` to `{campaignWorkspacePath}/archive/{name}-{timestamp}/` and log it before chaining to step-01. In headless mode, default to **resume** (never silently clobber); archive-and-overwrite only when `--brief`/`--manifest` explicitly seeds a new campaign.
121
+ 4. **`campaign status`** (read-only) — load `{campaignWorkspacePath}/_campaign-state.yaml`, validate it via `campaign-validate-state.py`, then run `uv run scripts/campaign-status.py --state-file {campaignWorkspacePath}/_campaign-state.yaml` and display its summary (campaign name, current stage, completed-vs-total, per-status counts) followed by the last ~15 lines of `{campaignWorkspacePath}/_campaign-decision-log.md` for the recent decision trail, then stop. No backup, no mutation, no chaining. Exit 0 (or 9 if the state is unrecoverable).
@@ -0,0 +1,191 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "campaign-state",
4
+ "description": "Schema for _campaign-state.yaml — single source of truth for campaign orchestration state",
5
+ "type": "object",
6
+ "required": ["campaign", "skills", "dependency_graph"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "campaign": {
10
+ "type": "object",
11
+ "required": ["name", "started_at", "last_updated", "current_stage", "quality_gate", "health_findings_queue"],
12
+ "additionalProperties": false,
13
+ "properties": {
14
+ "name": {
15
+ "type": "string"
16
+ },
17
+ "started_at": {
18
+ "type": "string",
19
+ "format": "date-time"
20
+ },
21
+ "last_updated": {
22
+ "type": "string",
23
+ "format": "date-time"
24
+ },
25
+ "current_stage": {
26
+ "type": "integer",
27
+ "minimum": 0,
28
+ "maximum": 10
29
+ },
30
+ "directive_path": {
31
+ "type": "string"
32
+ },
33
+ "quality_gate": {
34
+ "type": "object",
35
+ "required": ["hard", "soft_target", "soft_fallback"],
36
+ "additionalProperties": false,
37
+ "properties": {
38
+ "hard": {
39
+ "type": "string"
40
+ },
41
+ "soft_target": {
42
+ "type": "number"
43
+ },
44
+ "soft_fallback": {
45
+ "type": "number"
46
+ }
47
+ }
48
+ },
49
+ "health_findings_queue": {
50
+ "type": "string",
51
+ "enum": ["local", "improvement"]
52
+ },
53
+ "architecture_doc_path": {
54
+ "type": ["string", "null"],
55
+ "description": "Path to the architecture document consumed by the verify (Stage 7) and refine (Stage 8) stages. Sourced at setup; null when the campaign runs no verification."
56
+ },
57
+ "capstone": {
58
+ "type": ["object", "null"],
59
+ "additionalProperties": false,
60
+ "description": "Capstone (stack) outcome composed from all completed campaign skills (Tier A and Tier B). Campaign-level summary; the composed skill itself lives at skill_path.",
61
+ "properties": {
62
+ "skill_path": {
63
+ "type": ["string", "null"]
64
+ },
65
+ "quality_score": {
66
+ "type": ["number", "null"]
67
+ },
68
+ "verified": {
69
+ "type": ["boolean", "null"]
70
+ },
71
+ "completed_at": {
72
+ "type": ["string", "null"],
73
+ "format": "date-time"
74
+ }
75
+ }
76
+ },
77
+ "verification": {
78
+ "type": ["object", "null"],
79
+ "additionalProperties": false,
80
+ "description": "Stage 7 verify-stack summary. Detailed findings live in the external report at report_path.",
81
+ "properties": {
82
+ "report_path": {
83
+ "type": ["string", "null"]
84
+ },
85
+ "overall_verdict": {
86
+ "type": ["string", "null"],
87
+ "enum": ["Verified", "Plausible", "Risky", "Blocked", null]
88
+ },
89
+ "coverage_percentage": {
90
+ "type": ["number", "null"]
91
+ },
92
+ "recommendation_count": {
93
+ "type": ["integer", "null"]
94
+ }
95
+ }
96
+ },
97
+ "refinement": {
98
+ "type": ["object", "null"],
99
+ "additionalProperties": false,
100
+ "description": "Stage 8 refine-architecture summary. The refined document lives at refined_path.",
101
+ "properties": {
102
+ "refined_path": {
103
+ "type": ["string", "null"]
104
+ },
105
+ "gap_count": {
106
+ "type": ["integer", "null"]
107
+ },
108
+ "issue_count": {
109
+ "type": ["integer", "null"]
110
+ },
111
+ "improvement_count": {
112
+ "type": ["integer", "null"]
113
+ }
114
+ }
115
+ }
116
+ }
117
+ },
118
+ "skills": {
119
+ "type": "array",
120
+ "items": {
121
+ "type": "object",
122
+ "required": ["name", "status", "tier"],
123
+ "additionalProperties": false,
124
+ "properties": {
125
+ "name": {
126
+ "type": "string"
127
+ },
128
+ "status": {
129
+ "type": "string",
130
+ "enum": ["pending", "active", "completed", "failed", "skipped"]
131
+ },
132
+ "depends_on": {
133
+ "type": "array",
134
+ "items": {
135
+ "type": "string"
136
+ }
137
+ },
138
+ "tier": {
139
+ "type": "string",
140
+ "enum": ["A", "B"]
141
+ },
142
+ "pin": {
143
+ "type": ["string", "null"]
144
+ },
145
+ "brief_path": {
146
+ "type": ["string", "null"]
147
+ },
148
+ "skill_path": {
149
+ "type": ["string", "null"]
150
+ },
151
+ "quality_score": {
152
+ "type": ["number", "null"]
153
+ },
154
+ "workarounds_applied": {
155
+ "type": "array",
156
+ "items": {
157
+ "type": "string"
158
+ }
159
+ },
160
+ "started_at": {
161
+ "type": ["string", "null"],
162
+ "format": "date-time"
163
+ },
164
+ "completed_at": {
165
+ "type": ["string", "null"],
166
+ "format": "date-time"
167
+ },
168
+ "commit_sha": {
169
+ "type": ["string", "null"]
170
+ }
171
+ }
172
+ }
173
+ },
174
+ "dependency_graph": {
175
+ "type": "object",
176
+ "required": ["execution_order", "circular_deps_detected"],
177
+ "additionalProperties": false,
178
+ "properties": {
179
+ "execution_order": {
180
+ "type": "array",
181
+ "items": {
182
+ "type": "string"
183
+ }
184
+ },
185
+ "circular_deps_detected": {
186
+ "type": "boolean"
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,75 @@
1
+ # DO NOT EDIT -- overwritten on every update.
2
+ #
3
+ # Workflow customization surface for skf-campaign.
4
+ # Team overrides: _bmad/custom/skf-campaign.toml (under {project-root})
5
+ # Personal overrides: _bmad/custom/skf-campaign.user.toml (under {project-root})
6
+
7
+ [workflow]
8
+
9
+ # --- Configurable below. Overrides merge per BMad structural rules: ---
10
+ # scalars: override wins • arrays (persistent_facts, activation_steps_*): append
11
+ # arrays-of-tables with `code`/`id`: replace matching items, append new ones.
12
+
13
+ # Steps to run before the standard activation (config load, customization
14
+ # resolve). Overrides append. Use for org-wide pre-flight checks (auth,
15
+ # network, compliance) that must precede any orchestration work.
16
+
17
+ activation_steps_prepend = []
18
+
19
+ # Steps to run after activation but before the first stage executes.
20
+ # Overrides append. Use for context loads or banner customization that
21
+ # should run once activation completes successfully.
22
+
23
+ activation_steps_append = []
24
+
25
+ # Persistent facts the workflow keeps in mind for the whole campaign
26
+ # (house style, naming conventions, quality guardrails). These are injected
27
+ # into every per-skill kickoff, so project-scoped house facts propagate to
28
+ # the whole campaign. Overrides append.
29
+ #
30
+ # Each entry is either:
31
+ # - a literal sentence, e.g. "All skills must cite their upstream source."
32
+ # - a file reference prefixed with `file:`, e.g.
33
+ # "file:{project-root}/docs/skill-style.md" (globs supported; file
34
+ # contents are loaded and treated as facts).
35
+
36
+ persistent_facts = [
37
+ "file:{project-root}/**/project-context.md",
38
+ ]
39
+
40
+ # --- Workspace path ---
41
+ #
42
+ # Where the campaign keeps its state, backup, brief, batch input, archive,
43
+ # and decision log. Empty string = use the bundled default
44
+ # `{forge_data_folder}/_campaign`. Override to relocate campaign artifacts
45
+ # (e.g. a shared volume) without editing every step file. Resolved once in
46
+ # On Activation as {campaignWorkspacePath}.
47
+
48
+ campaign_workspace_path = ""
49
+
50
+ # --- Quality gate ---
51
+ #
52
+ # Campaign-wide quality bar applied to every skill. Scalars override the
53
+ # bundled defaults; the per-campaign brief and any directive `## Quality
54
+ # Overrides` still take precedence at runtime. Override here to change the
55
+ # org-wide default without forking the skill.
56
+
57
+ quality_gate_hard = "zero-critical-high"
58
+ quality_gate_soft_target = 90
59
+ quality_gate_soft_fallback = 80
60
+
61
+ # --- Optional template overrides ---
62
+ #
63
+ # Lift the canonical template paths so orgs can substitute house-style copies
64
+ # without forking the skill. Empty string = use the bundled default under
65
+ # `templates/`.
66
+
67
+ report_template_path = ""
68
+ kickoff_template_path = ""
69
+ brief_template_path = ""
70
+
71
+ # Optional post-completion hook. When non-empty, the workflow invokes:
72
+ # <on_complete> --report-path=<path-to-campaign-report.md>
73
+ # after the campaign report is finalized (step-11). Failures are logged to
74
+ # the decision log but never fail the campaign. Empty = no-op.
75
+ on_complete = ""
@@ -0,0 +1,15 @@
1
+ code: CA
2
+ name: skf-campaign
3
+ description: "Campaign orchestration — multi-library skill production with dependency tracking"
4
+ version: "2.0.0"
5
+ trigger: campaign
6
+ parent_module: skf
7
+
8
+ # Canonical internal filenames for the campaign workspace. Fixed contract (the
9
+ # step chain references these names directly and test-skf-campaign-state guards
10
+ # them against drift) — NOT an operator-override surface; customize.toml is the
11
+ # only customization mechanism.
12
+ config:
13
+ state_file: "_campaign-state.yaml"
14
+ backup_file: "_campaign-state.yaml.bak"
15
+ directive_file: "_campaign-directive.md"
@@ -0,0 +1,67 @@
1
+ <!-- Config: communicate in {communication_language}. -->
2
+
3
+ # Campaign Contracts
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.
6
+
7
+ ## Exit Codes
8
+
9
+ Every HARD HALT exits with a stable, documented code so headless automators can branch on the failure class without grepping message text:
10
+
11
+ | Code | Meaning | Raised by |
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) |
21
+ | 9 | corrupt-state | step-resume §1 (primary unrecoverable, `.bak` also invalid) |
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
+ | 11 | export-cancelled | step-10 §4 (operator chose `[C]ancel` — graceful, resumable) |
24
+ | 12 | user-cancelled | any interactive gate (operator typed `cancel` / `exit` / `:q` at a prompt between Setup and the Export gate — graceful, resumable) |
25
+
26
+ ## Result Contract on HARD HALT
27
+
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
+
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>"}
32
+ ```
33
+
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.
35
+
36
+ ## State Contract
37
+
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`
46
+
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.
48
+
49
+ ## Campaign Headless Envelope
50
+
51
+ When `{headless_mode}` is true, the final step emits a single-line JSON envelope on stdout:
52
+
53
+ ```
54
+ SKF_CAMPAIGN_RESULT_JSON: {"status":"success|error","skills_completed":0,"skills_failed":0,"quality_scores":{},"campaign_report_path":"","decision_log":"","duration":""}
55
+ ```
56
+
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.
58
+
59
+ ## Headless Progress Events
60
+
61
+ When `{headless_mode}` is true, emit a single-line JSON progress event to **stderr** at each step's entry, exit, and HARD HALT, so schedulers stream live progress instead of post-mortem-parsing the final envelope:
62
+
63
+ - entry: `{"stage":N,"name":"<slug>","status":"start"}`
64
+ - exit (just before chaining): `{"stage":N,"name":"<slug>","status":"done"}`
65
+ - on HARD HALT: `{"stage":N,"name":"<slug>","status":"halt","exit":<code>}` instead of `"done"`
66
+
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.
@@ -0,0 +1,32 @@
1
+ ---
2
+ stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
3
+ directiveFile: '_campaign-directive.md'
4
+ ---
5
+
6
+ # Campaign Directive Specification
7
+
8
+ Canonical contract for the campaign directive (`_campaign-directive.md`) — a
9
+ file-based standing directive holding campaign-wide policy. When a step loads
10
+ `campaign.directive_path` it re-reads the file fresh from disk at stage entry
11
+ (no caching, so operator edits between stages are picked up) and applies the
12
+ sections below as campaign-wide context. UTF-8 markdown; frontmatter not
13
+ required; default filename `_campaign-directive.md`.
14
+
15
+ ## Recognized Sections
16
+
17
+ All optional — the directive may hold any combination of these, or none:
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.
21
+ - **`## Pipeline Flags`** — per-skill or campaign-wide pipeline modifiers.
22
+ - **`## Notes`** — free-form operator context for the agent processing the campaign.
23
+
24
+ Any heading not listed above is treated as general guidance: read it and apply
25
+ judgment based on the content. This lets operators add ad-hoc context without
26
+ modifying this specification.
27
+
28
+ ## Absence Behavior
29
+
30
+ - If `campaign.directive_path` is not set in the state file: no error, proceed with defaults
31
+ - If `campaign.directive_path` is set but the file does not exist at that path: no error, proceed with defaults
32
+ - The directive is always optional — a campaign runs identically without one
@@ -0,0 +1,35 @@
1
+ ---
2
+ # Note: `shared/health-check.md` resolves relative to the SKF module root
3
+ # ({project-root}/_bmad/skf/ when installed, {project-root}/src/ during
4
+ # development), NOT relative to this step file.
5
+ nextStepFile: 'shared/health-check.md'
6
+ stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
7
+ ---
8
+
9
+ <!-- Config: communicate in {communication_language}. -->
10
+
11
+ # Workflow Health Check
12
+
13
+ ## STEP GOAL:
14
+
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.
16
+
17
+ ## Rules
18
+
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.
21
+
22
+ ## MANDATORY SEQUENCE
23
+
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.