bmad-module-skill-forge 2.2.0 → 4.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 (437) 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 +143 -18
  18. package/docs/verifying-a-skill.md +87 -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 +3 -3
  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 +31 -59
  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 +233 -64
  64. package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
  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 +772 -0
  69. package/src/shared/scripts/skf-comention-pairs.py +1333 -123
  70. package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
  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 +21 -3
  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 +197 -55
  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 +5247 -96
  86. package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
  87. package/src/shared/scripts/skf-extraction-snapshot.py +664 -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 +469 -205
  92. package/src/shared/scripts/skf-github-fetch.py +365 -0
  93. package/src/shared/scripts/skf-github-probe.py +700 -0
  94. package/src/shared/scripts/skf-hash-content.py +180 -20
  95. package/src/shared/scripts/skf-language-corpora.py +1 -1
  96. package/src/shared/scripts/skf-load-provenance.py +161 -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 +143 -20
  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 +176 -28
  105. package/src/shared/scripts/skf-promote-staged.py +430 -0
  106. package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
  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 +155 -14
  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 +787 -121
  127. package/src/shared/scripts/skf-tessl-review.py +706 -0
  128. package/src/shared/scripts/skf-update-active-symlink.py +120 -67
  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 +23 -2
  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 +3945 -152
  138. package/src/shared/scripts/skf-write-skill-brief.py +406 -25
  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 +28 -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/health-check.md +2 -0
  168. package/src/skf-audit-skill/references/init.md +149 -210
  169. package/src/skf-audit-skill/references/re-index.md +103 -110
  170. package/src/skf-audit-skill/references/report.md +96 -33
  171. package/src/skf-audit-skill/references/semantic-diff.md +34 -25
  172. package/src/skf-audit-skill/references/severity-classify.md +79 -65
  173. package/src/skf-audit-skill/references/severity-rules.md +15 -6
  174. package/src/skf-audit-skill/references/structural-diff.md +80 -90
  175. package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
  176. package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
  177. package/src/skf-brief-skill/SKILL.md +37 -26
  178. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  179. package/src/skf-brief-skill/assets/scope-templates.md +18 -25
  180. package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
  181. package/src/skf-brief-skill/customize.toml +48 -20
  182. package/src/skf-brief-skill/references/analyze-target.md +144 -127
  183. package/src/skf-brief-skill/references/confirm-brief.md +11 -9
  184. package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
  185. package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
  186. package/src/skf-brief-skill/references/gather-intent.md +87 -182
  187. package/src/skf-brief-skill/references/headless-args.md +84 -16
  188. package/src/skf-brief-skill/references/health-check.md +1 -1
  189. package/src/skf-brief-skill/references/invocation-contract.md +18 -13
  190. package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
  191. package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
  192. package/src/skf-brief-skill/references/scope-definition.md +123 -47
  193. package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
  194. package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
  195. package/src/skf-brief-skill/references/version-resolution.md +7 -10
  196. package/src/skf-brief-skill/references/write-brief.md +66 -57
  197. package/src/skf-campaign/SKILL.md +31 -34
  198. package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
  199. package/src/skf-campaign/customize.toml +47 -16
  200. package/src/skf-campaign/references/campaign-contracts.md +39 -29
  201. package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
  202. package/src/skf-campaign/references/health-check.md +7 -16
  203. package/src/skf-campaign/references/step-01-setup.md +46 -84
  204. package/src/skf-campaign/references/step-02-strategy.md +31 -30
  205. package/src/skf-campaign/references/step-03-pins.md +26 -18
  206. package/src/skf-campaign/references/step-04-provenance.md +24 -23
  207. package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
  208. package/src/skf-campaign/references/step-06-batch.md +41 -34
  209. package/src/skf-campaign/references/step-07-capstone.md +40 -25
  210. package/src/skf-campaign/references/step-08-verify.md +28 -29
  211. package/src/skf-campaign/references/step-09-refine.md +29 -30
  212. package/src/skf-campaign/references/step-10-export.md +48 -36
  213. package/src/skf-campaign/references/step-11-maintenance.md +31 -34
  214. package/src/skf-campaign/references/step-resume.md +37 -68
  215. package/src/skf-campaign/scripts/campaign-deps.py +57 -18
  216. package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
  217. package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
  218. package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
  219. package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
  220. package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
  221. package/src/skf-campaign/scripts/campaign-report.py +204 -37
  222. package/src/skf-campaign/scripts/campaign-state.py +1128 -0
  223. package/src/skf-campaign/scripts/campaign-status.py +10 -74
  224. package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
  225. package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
  226. package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
  227. package/src/skf-campaign/templates/campaign-report-template.md +5 -1
  228. package/src/skf-campaign/templates/kickoff-template.md +0 -14
  229. package/src/skf-create-skill/SKILL.md +27 -20
  230. package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
  231. package/src/skf-create-skill/assets/skill-sections.md +21 -11
  232. package/src/skf-create-skill/customize.toml +20 -8
  233. package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
  234. package/src/skf-create-skill/references/batch-mode.md +81 -0
  235. package/src/skf-create-skill/references/ccc-index-check.md +27 -0
  236. package/src/skf-create-skill/references/compile.md +119 -70
  237. package/src/skf-create-skill/references/component-extraction.md +113 -91
  238. package/src/skf-create-skill/references/enrich.md +41 -23
  239. package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
  240. package/src/skf-create-skill/references/extract.md +204 -162
  241. package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
  242. package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
  243. package/src/skf-create-skill/references/extraction-patterns.md +588 -201
  244. package/src/skf-create-skill/references/generate-artifacts.md +145 -93
  245. package/src/skf-create-skill/references/health-check.md +1 -1
  246. package/src/skf-create-skill/references/load-brief.md +53 -36
  247. package/src/skf-create-skill/references/relabel-rule.md +12 -0
  248. package/src/skf-create-skill/references/report.md +37 -57
  249. package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
  250. package/src/skf-create-skill/references/step-auto-shard.md +20 -93
  251. package/src/skf-create-skill/references/step-doc-rot.md +44 -60
  252. package/src/skf-create-skill/references/step-doc-sources.md +28 -19
  253. package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
  254. package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
  255. package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
  256. package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
  257. package/src/skf-create-skill/references/validate.md +173 -99
  258. package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
  259. package/src/skf-create-stack-skill/SKILL.md +29 -56
  260. package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
  261. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
  262. package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
  263. package/src/skf-create-stack-skill/customize.toml +23 -12
  264. package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
  265. package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
  266. package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
  267. package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
  268. package/src/skf-create-stack-skill/references/generate-output.md +138 -128
  269. package/src/skf-create-stack-skill/references/init.md +90 -32
  270. package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
  271. package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
  272. package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
  273. package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
  274. package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
  275. package/src/skf-create-stack-skill/references/report.md +39 -33
  276. package/src/skf-create-stack-skill/references/validate.md +20 -39
  277. package/src/skf-drop-skill/SKILL.md +28 -51
  278. package/src/skf-drop-skill/customize.toml +44 -30
  279. package/src/skf-drop-skill/references/execute.md +83 -146
  280. package/src/skf-drop-skill/references/health-check.md +2 -0
  281. package/src/skf-drop-skill/references/invocation-contract.md +64 -0
  282. package/src/skf-drop-skill/references/report.md +46 -30
  283. package/src/skf-drop-skill/references/select.md +122 -124
  284. package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
  285. package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
  286. package/src/skf-export-skill/SKILL.md +40 -62
  287. package/src/skf-export-skill/assets/managed-section-format.md +14 -50
  288. package/src/skf-export-skill/assets/snippet-format.md +3 -1
  289. package/src/skf-export-skill/customize.toml +42 -13
  290. package/src/skf-export-skill/references/generate-snippet.md +54 -46
  291. package/src/skf-export-skill/references/health-check.md +2 -0
  292. package/src/skf-export-skill/references/invocation-contract.md +15 -0
  293. package/src/skf-export-skill/references/load-skill.md +88 -62
  294. package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
  295. package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
  296. package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
  297. package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
  298. package/src/skf-export-skill/references/package.md +15 -73
  299. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
  300. package/src/skf-export-skill/references/result-envelope.md +31 -16
  301. package/src/skf-export-skill/references/summary.md +41 -17
  302. package/src/skf-export-skill/references/token-report.md +4 -12
  303. package/src/skf-export-skill/references/update-context.md +118 -237
  304. package/src/skf-forger/SKILL.md +61 -37
  305. package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
  306. package/src/skf-forger/customize.toml +43 -0
  307. package/src/skf-forger/references/pipeline-mode.md +73 -18
  308. package/src/skf-forger/scripts/forge-status.py +299 -0
  309. package/src/skf-forger/scripts/parse-pipeline.py +322 -54
  310. package/src/skf-forger/scripts/pipeline-gate.py +428 -0
  311. package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
  312. package/src/skf-quick-skill/SKILL.md +54 -53
  313. package/src/skf-quick-skill/assets/skill-template.md +4 -2
  314. package/src/skf-quick-skill/customize.toml +51 -20
  315. package/src/skf-quick-skill/references/batch-mode.md +74 -21
  316. package/src/skf-quick-skill/references/compile.md +30 -31
  317. package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
  318. package/src/skf-quick-skill/references/finalize.md +69 -18
  319. package/src/skf-quick-skill/references/halt-contract.md +41 -29
  320. package/src/skf-quick-skill/references/health-check.md +5 -3
  321. package/src/skf-quick-skill/references/quick-extract.md +114 -45
  322. package/src/skf-quick-skill/references/registry-resolution.md +9 -64
  323. package/src/skf-quick-skill/references/resolve-target.md +116 -66
  324. package/src/skf-quick-skill/references/write-and-validate.md +79 -40
  325. package/src/skf-refine-architecture/SKILL.md +43 -53
  326. package/src/skf-refine-architecture/customize.toml +49 -20
  327. package/src/skf-refine-architecture/references/compile.md +97 -54
  328. package/src/skf-refine-architecture/references/exit-codes.md +30 -0
  329. package/src/skf-refine-architecture/references/finding-storage.md +21 -0
  330. package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
  331. package/src/skf-refine-architecture/references/improvements.md +14 -20
  332. package/src/skf-refine-architecture/references/init.md +98 -57
  333. package/src/skf-refine-architecture/references/issue-detection.md +32 -39
  334. package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
  335. package/src/skf-refine-architecture/references/report.md +65 -54
  336. package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
  337. package/src/skf-rename-skill/SKILL.md +19 -54
  338. package/src/skf-rename-skill/customize.toml +32 -26
  339. package/src/skf-rename-skill/references/execute.md +230 -184
  340. package/src/skf-rename-skill/references/exit-codes.md +7 -7
  341. package/src/skf-rename-skill/references/health-check.md +1 -7
  342. package/src/skf-rename-skill/references/invocation-contract.md +25 -0
  343. package/src/skf-rename-skill/references/report.md +63 -52
  344. package/src/skf-rename-skill/references/select.md +197 -115
  345. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
  346. package/src/skf-setup/SKILL.md +26 -26
  347. package/src/skf-setup/customize.toml +14 -10
  348. package/src/skf-setup/references/auto-index.md +50 -63
  349. package/src/skf-setup/references/ccc-index.md +29 -90
  350. package/src/skf-setup/references/detect-and-tier.md +58 -67
  351. package/src/skf-setup/references/health-check.md +2 -0
  352. package/src/skf-setup/references/invocation-contract.md +14 -0
  353. package/src/skf-setup/references/report.md +43 -166
  354. package/src/skf-setup/references/tier-rules.md +5 -5
  355. package/src/skf-setup/references/write-config.md +34 -57
  356. package/src/skf-test-skill/SKILL.md +17 -57
  357. package/src/skf-test-skill/assets/output-section-formats.md +33 -34
  358. package/src/skf-test-skill/customize.toml +27 -13
  359. package/src/skf-test-skill/references/coherence-check.md +134 -62
  360. package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
  361. package/src/skf-test-skill/references/coverage-check.md +209 -264
  362. package/src/skf-test-skill/references/detect-mode.md +5 -5
  363. package/src/skf-test-skill/references/external-validators.md +94 -82
  364. package/src/skf-test-skill/references/health-check.md +6 -7
  365. package/src/skf-test-skill/references/init.md +164 -91
  366. package/src/skf-test-skill/references/invocation-contract.md +66 -0
  367. package/src/skf-test-skill/references/migration-section-rules.md +5 -2
  368. package/src/skf-test-skill/references/report.md +164 -142
  369. package/src/skf-test-skill/references/score.md +80 -107
  370. package/src/skf-test-skill/references/scoring-rules.md +42 -155
  371. package/src/skf-test-skill/references/source-access-protocol.md +18 -94
  372. package/src/skf-test-skill/references/step-hard-gate.md +51 -33
  373. package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
  374. package/src/skf-test-skill/scripts/build-result-context.py +380 -0
  375. package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
  376. package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
  377. package/src/skf-test-skill/scripts/compute-score.py +223 -45
  378. package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
  379. package/src/skf-test-skill/scripts/hard-gate.py +203 -0
  380. package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
  381. package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
  382. package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
  383. package/src/skf-test-skill/scripts/score-signatures.py +479 -0
  384. package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
  385. package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
  386. package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
  387. package/src/skf-test-skill/templates/test-report-template.md +21 -15
  388. package/src/skf-update-skill/SKILL.md +38 -25
  389. package/src/skf-update-skill/customize.toml +27 -12
  390. package/src/skf-update-skill/references/detect-changes.md +253 -223
  391. package/src/skf-update-skill/references/gap-driven.md +336 -0
  392. package/src/skf-update-skill/references/health-check.md +31 -5
  393. package/src/skf-update-skill/references/init.md +254 -84
  394. package/src/skf-update-skill/references/invocation-contract.md +12 -0
  395. package/src/skf-update-skill/references/manual-section-rules.md +6 -23
  396. package/src/skf-update-skill/references/merge.md +182 -49
  397. package/src/skf-update-skill/references/re-extract.md +53 -262
  398. package/src/skf-update-skill/references/report.md +102 -35
  399. package/src/skf-update-skill/references/write.md +357 -155
  400. package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
  401. package/src/skf-verify-stack/SKILL.md +23 -42
  402. package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
  403. package/src/skf-verify-stack/customize.toml +55 -22
  404. package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
  405. package/src/skf-verify-stack/references/coverage.md +67 -64
  406. package/src/skf-verify-stack/references/exit-codes.md +21 -8
  407. package/src/skf-verify-stack/references/init.md +107 -62
  408. package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
  409. package/src/skf-verify-stack/references/integrations.md +106 -65
  410. package/src/skf-verify-stack/references/report.md +59 -42
  411. package/src/skf-verify-stack/references/requirements.md +32 -9
  412. package/src/skf-verify-stack/references/synthesize.md +53 -39
  413. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
  414. package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
  415. package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
  416. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
  417. package/tools/cli/commands/install.js +11 -4
  418. package/tools/cli/commands/status.js +15 -3
  419. package/tools/cli/commands/update.js +11 -4
  420. package/tools/cli/lib/tool-check.js +320 -0
  421. package/tools/cli/lib/version-check.js +22 -9
  422. package/tools/cli/skf-cli.js +4 -1
  423. package/.gitattributes +0 -16
  424. package/.nvmrc +0 -1
  425. package/docs/_internal/RELEASING.md +0 -679
  426. package/release-audits/v1.0.0-launch-audit.md +0 -728
  427. package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
  428. package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
  429. package/src/skf-campaign/manifest.yaml +0 -15
  430. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
  431. package/src/skf-create-skill/references/ecosystem-check.md +0 -74
  432. package/src/skf-drop-skill/references/headless-contract.md +0 -18
  433. package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
  434. package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
  435. package/src/skf-update-skill/references/validate.md +0 -124
  436. package/tools/validate-docs-drift.js +0 -233
  437. package/tools/validate-docs-links.js +0 -665
@@ -1,40 +1,21 @@
1
1
  ---
2
2
  nextStepFile: 'report.md'
3
- versionPathsKnowledge: 'knowledge/version-paths.md'
4
- managedSectionLogic: 'skf-export-skill/assets/managed-section-format.md'
5
- # Resolve `{manifestOpsHelper}` by probing `{manifestOpsProbeOrder}` in
6
- # order (installed SKF module path first, src/ dev-checkout fallback);
7
- # first existing path wins. §2 calls it for atomic manifest deprecate /
8
- # remove with v1→v2 migration handled internally — letting the LLM hand-
9
- # roll JSON manipulation risks key-order drift, indent regressions, and
10
- # write-atomicity bugs. HALT if neither candidate exists.
11
- manifestOpsProbeOrder:
12
- - '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
13
- - '{project-root}/src/shared/scripts/skf-manifest-ops.py'
14
- # Resolve `{rebuildManagedSectionsHelper}` similarly. §3 calls it (replace
15
- # action) for the surgical between-marker rewrite — the LLM still computes
16
- # the new managed section text, but the file mutation is deterministic
17
- # (atomic temp-file + rename, marker preservation, post-write verify).
18
- rebuildManagedSectionsProbeOrder:
19
- - '{project-root}/_bmad/skf/shared/scripts/skf-rebuild-managed-sections.py'
20
- - '{project-root}/src/shared/scripts/skf-rebuild-managed-sections.py'
21
- # Resolve `{updateActiveSymlinkHelper}` similarly. §4 uses it (update
22
- # action) to atomically repoint `{skill_group}/active` after a version-level
23
- # purge deletes the version the symlink pointed at — the helper does a
24
- # temp-symlink + os.replace flip so concurrent readers never see a missing
25
- # link. Matches skf-update-skill/references/write.md and
26
- # skf-rename-skill/references/execute.md. HALT if neither candidate exists.
3
+ # {updateActiveSymlinkHelper}: the §4 `active` repoint.
27
4
  updateActiveSymlinkProbeOrder:
28
5
  - '{project-root}/_bmad/skf/shared/scripts/skf-update-active-symlink.py'
29
6
  - '{project-root}/src/shared/scripts/skf-update-active-symlink.py'
30
- # Standalone single-line result-envelope contract emitted at every headless
31
- # HALT in this step. Loaded in §1 so no error path depends on SKILL.md
32
- # remaining in context under compaction.
33
- headlessContract: 'headless-contract.md'
34
- # Deterministic recursive byte sizing + human formatting for `disk_freed`
35
- # (§4). Bundled with this skill; the same helper backs select.md §9b's
36
- # blast-radius preview, so gate and report agree on the method.
7
+ # {skillInventoryHelper}: the §4 guarded delete and `resolve`.
8
+ skillInventoryProbeOrder:
9
+ - '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
10
+ - '{project-root}/src/shared/scripts/skf-skill-inventory.py'
11
+ # {dropRosterHelper}: the §5 context-file check.
12
+ dropRosterHelper: 'scripts/drop-roster.py'
13
+ # {dirSizesHelper}: the §4 `disk_freed` label.
37
14
  dirSizesHelper: 'scripts/dir-sizes.py'
15
+ # {atomicWriteHelper}: the §4 manual fixes that point `active` at a version.
16
+ atomicWriteProbeOrder:
17
+ - '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py'
18
+ - '{project-root}/src/shared/scripts/skf-atomic-write.py'
38
19
  ---
39
20
 
40
21
  <!-- Config: communicate in {communication_language}. -->
@@ -51,24 +32,27 @@ Execute the drop decisions recorded in step 1: update the export manifest, rebui
51
32
  - Do not re-prompt the user — decisions were made in step 1
52
33
  - Do not delete files in deprecate mode; do not widen deletion scope beyond `affected_directories`
53
34
  - Report each stage's outcome as it completes
35
+ - Record each degraded outcome as a warning the moment it happens, with the text the section names, so the envelope and the result record carry it: `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning '<the warning>'`, a single quote in it written as a backtick. If the command fails, go on: only that entry is lost.
54
36
 
55
37
  ## MANDATORY SEQUENCE
56
38
 
57
- ### 1. Re-read Version-Paths Knowledge
39
+ ### 1. Halt Envelope
58
40
 
59
- Read `{versionPathsKnowledge}` again and confirm the templates and management operations. This ensures the execution step uses the same rules as the selection step even when run in isolation.
41
+ Every HALT in this step names its exit code, `halt_reason` and phase. In headless mode it also prints its envelope through the shared emitter before it stops (`{emitEnvelopeHelper}` and `{run_dir}` come from SKILL.md On-Activation §4): stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "exit_code": <code>}` plus the envelope fields the site names (and `"path"` when the halt names one), then run
60
42
 
61
- Also read `{managedSectionLogic}` for the format template, the four-case logic, and the skill index rebuild rules that will be reused in section 3.
43
+ ```bash
44
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-drop-skill --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
45
+ ```
62
46
 
63
- If `{headless_mode}` is true, also read `{headlessContract}` now — it defines the single-line result envelope the error paths below emit, so the shape is in context even if SKILL.md was compacted out.
47
+ and display the line it prints verbatim. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. The emitter gives every field the site leaves out its default (`null`, `[]` or `false`) and checks `exit_code` against `halt_reason`. If it exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message, emits nothing and then deletes the run folder (`rm -rf "{run_dir}"`), which nothing reads after it.
64
48
 
65
49
  ### 2. Update Export Manifest
66
50
 
67
- **If `target_in_manifest == false`** (draft skill discovered only by on-disk scan): Skip this section entirely. There is no manifest entry to deprecate or delete. Set `manifest_updated = false` and proceed directly to section 3. Step-01 forced `drop_mode = "purge"` and `is_skill_level = true` in this case, so the subsequent sections will hard-delete the on-disk directories without any manifest interaction.
51
+ **If `target_in_manifest == false`** (draft skill discovered only by on-disk scan): Skip this section entirely. There is no manifest entry to deprecate or delete. Set `manifest_updated = false` and proceed directly to section 3.
68
52
 
69
53
  **If `target_in_manifest == true`:**
70
54
 
71
- **Resolve `{manifestOpsHelper}`** from `{manifestOpsProbeOrder}`; first existing path wins. HALT (exit code 4, `halt_reason: "manifest-write-failed"`) if no candidate exists — atomic manifest mutation must go through the helper.
55
+ Write through the `{manifestOpsHelper}` On-Activation §4 resolved: atomic manifest mutation always goes through the helper.
72
56
 
73
57
  **If `is_skill_level == false` (version-level drop):**
74
58
 
@@ -82,88 +66,56 @@ The helper sets `exports.{target_skill}.versions.{version}.status = "deprecated"
82
66
 
83
67
  **If `is_skill_level == true` (skill-level drop):**
84
68
 
85
- ```bash
86
- python3 {manifestOpsHelper} {skills_output_folder} remove {target_skill}
87
- ```
88
-
89
- The helper deletes the `exports.{target_skill}` key entirely; other entries are untouched.
90
-
91
- Set context flag `manifest_updated = true`.
92
-
93
- **On error (helper non-zero exit):**
69
+ - **Deprecate (`drop_mode == "deprecate"`):** mark every version deprecated and keep the entry, so the soft drop stays reversible. With no version, the helper sets the `status` of every version under `exports.{target_skill}.versions` to `"deprecated"` and leaves `active_version` as it is:
94
70
 
95
- - Do not proceed to section 3
96
- - Report: "**Manifest update failed:** {captured stderr}. No files were deleted and platform context files were not rebuilt. The manifest is in its pre-drop state — rerun the workflow once the underlying issue is resolved."
97
- - Store `manifest_updated = false` and jump to section 6. In headless mode, emit the error envelope per `{headlessContract}` with `halt_reason: "manifest-write-failed"` and exit code 4.
71
+ ```bash
72
+ python3 {manifestOpsHelper} {skills_output_folder} deprecate {target_skill}
73
+ ```
98
74
 
99
- ### 3. Rebuild Context Files
100
-
101
- Load the `ides` list from `config.yaml`. The installer writes IDE identifiers — these must be mapped to context files and skill roots using the "IDE → Context File Mapping" table in `{managedSectionLogic}`.
102
-
103
- **Resolve `target_context_files`** using the canonical mapping table in `{managedSectionLogic}`:
104
-
105
- 1. For each entry in `config.yaml.ides`, look up its `context_file` and `skill_root` from the mapping table
106
- 2. For any entry not found in the table, default to `{unknownIdeDefaultContextFile}` / `{unknownIdeDefaultSkillRoot}` (resolved at SKILL.md On Activation §3 from `workflow.unknown_ide_default_*`, bundled defaults `AGENTS.md` / `.agents/skills/`) and emit a warning: "Unknown IDE '{value}' in config.yaml — defaulting to {unknownIdeDefaultContextFile}"
107
- 3. Deduplicate by `context_file` — when multiple IDEs map to the same context file, use the first configured IDE's `skill_root`
108
- 4. If `config.yaml.ides` is absent or the mapping yields an empty list, fall back to `[{context_file: "{unknownIdeDefaultContextFile}", skill_root: "{unknownIdeDefaultSkillRoot}"}]` and emit a note: "No IDEs configured in config.yaml — defaulting to {unknownIdeDefaultContextFile}"
75
+ - **Purge:** the helper deletes the `exports.{target_skill}` key entirely; other entries are untouched:
109
76
 
110
- Store the result as `target_context_files` for this section.
77
+ ```bash
78
+ python3 {manifestOpsHelper} {skills_output_folder} remove {target_skill}
79
+ ```
111
80
 
112
- For each entry in `target_context_files`:
81
+ When the helper exits 0 (`status: "ok"`), set context flag `manifest_updated = true`.
113
82
 
114
- 1. **Resolve target file** at `{context_file}`.
83
+ **On error (the helper exits non-zero):** HALT (exit code 4, `halt_reason: "manifest-write-failed"`, phase `execute:manifest-write`, path `{skills_output_folder}/.export-manifest.json`) in every mode, before section 3: "**Manifest update failed:** {the helper's `error`, `status: not_found` when the entry is no longer there, or its stderr when stdout holds no JSON}. Nothing was changed: no file was deleted, no context file was rebuilt, and the manifest is in its pre-drop state. Re-run the drop once the cause is fixed." The §1 halt envelope carries the resolved `skill`, `drop_mode` and `versions_affected`, with `manifest_updated: false`. The run ends here: step 3 never runs, so no success report is shown and no result file is written.
115
84
 
116
- 2. **Read the current file.**
117
- - If the file does not exist, skip this context file (nothing to rebuild — the file will be re-created next time export-skill runs)
118
- - If the file exists but contains no `<!-- SKF:BEGIN -->` marker, skip this context file (no managed section to rewrite)
119
- - If the file contains `<!-- SKF:BEGIN -->` but no matching `<!-- SKF:END -->`, record the error against that context file and continue to the next entry — do not halt the entire drop on a malformed context file. The manifest has already been updated in section 2 and is canonical state; the context file can be repaired manually and rebuilt on the next `[EX] Export Skill` run.
85
+ ### 3. Rebuild Context Files
120
86
 
121
- 3. **Build the exported skill set (version-aware, deprecated-excluded)** using the same logic as export-skill step 4 section 4b:
122
- - Read the manifest's `exports` object (already updated in section 2)
123
- - For each skill, resolve its `active_version`
124
- - If `versions.{active_version}.status == "deprecated"`, skip that skill entirely
125
- - The result is the set of `{skill-name, active_version}` pairs that should appear in the managed section
87
+ Rebuild the managed section of each context file step 1 resolved (`target_context_files`, from `resolve-targets` in select.md §9b) the way export-skill writes it, through the `{rebuildManagedSectionsHelper}` On-Activation §4 resolved.
126
88
 
127
- 4. **Resolve and filter snippets** using export-skill step 4 section 4c logic:
128
- - For each `{skill-name, active_version}` in the set, read `{skills_output_folder}/{skill-name}/{active_version}/{skill-name}/context-snippet.md`
129
- - If the file is missing, fall back to the `active` symlink path, then skip with a warning if still not found
130
- - Collect successful snippets into the skill index
89
+ A target's file is `{context_path}`, its `{context_file}` in `{project-root}`, and `{target_paths}` is the `{context_path}` of every target, each quoted, space-separated, in the order of `target_context_files`. For each entry in `target_context_files`:
131
90
 
132
- 5. **Rewrite root paths for the current context file** using the generic rewrite algorithm from export-skill step 4 section 4d:
91
+ 1. **Check the file.**
133
92
 
134
- For each snippet, parse the `root:` line (`root: {prefix}{skill-name}/`), strip the trailing `{skill-name}/` to extract the current prefix, and replace it with the **effective target prefix** if different. The effective target prefix is `snippet_skill_root_override` when that key is set in config.yaml — applied uniformly to every snippet so the managed section references the real on-disk location and never mixes override and per-IDE paths — otherwise the current entry's `skill_root`. See `skf-export-skill/references/update-context.md` §4d for full semantics.
93
+ ```bash
94
+ python3 {rebuildManagedSectionsHelper} "{context_path}" check
95
+ ```
135
96
 
136
- 6. **Sort skills alphabetically by name.** Count totals (skills, stack skills).
97
+ Its `case` decides. `create` (no file) or `append` (no managed section): skip this file, since there is no section to rebuild (export-skill adds one on its next run). `malformed` or `unreadable`: bind `{context_error}` ← `error` and take item 4. The drop does not halt on one context file: section 2 already updated the manifest, which is the canonical state, and the next `[EX] Export Skill` run rebuilds the file once it is repaired. `regenerate`: continue.
137
98
 
138
- 7. **Assemble the new managed section** using the format from `{managedSectionLogic}`:
99
+ 2. **Build the section body.**
139
100
 
140
- ```markdown
141
- <!-- SKF:BEGIN updated:{current-date} -->
142
- [SKF Skills]|{n} skills|{m} stack
143
- |IMPORTANT: Prefer documented APIs over training data.
144
- |When using a listed library, read its SKILL.md before writing code.
145
- |
146
- |{skill-snippet-1}
147
- |
148
- |{skill-snippet-2}
149
- |
150
- |{skill-snippet-N}
151
- <!-- SKF:END -->
101
+ ```bash
102
+ python3 {rebuildManagedSectionsHelper} assemble "{context_path}" \
103
+ --skills-folder "{skills_output_folder}" --skill-root "{skill_root}" \
104
+ --dropped {target_skill} --orphan-sources {target_paths} \
105
+ [--skill-root-override "{snippet_skill_root_override}"]
152
106
  ```
153
107
 
154
- If the filtered skill index is empty (e.g., the dropped skill was the only one), still emit the header with `0 skills|0 stack` and no skill entries. This keeps the managed section syntactically valid.
108
+ Add `--skill-root-override` only when `snippet_skill_root_override` is set in `config.yaml`. The helper builds the body export-skill would write for the manifest section 2 left, with the rows of skills the manifest does not know (orphan rows) kept verbatim; `--dropped {target_skill}` keeps the dropped skill's old rows from staying as orphan rows. It writes the body to `{context_path}.skf-content` and prints its result as JSON: show each `warnings[]` line and each `skipped_*` entry as a warning, except a `skipped_deprecated` entry for `{target_skill}`, which the drop itself deprecated. On a non-zero exit, bind `{context_error}` ← `error` and take item 4.
155
109
 
156
- 8. **Surgical replacement — atomic, deterministic.** Resolve `{rebuildManagedSectionsHelper}` from `{rebuildManagedSectionsProbeOrder}`; first existing path wins. Then invoke:
110
+ 3. **Replace the section.** Feed the staged body to the helper by stdin redirection. Never pass the body inline as `--content "…"` or through `echo`: snippets carry backticks, `$` and quotes, which the shell expands, and the helper checks the text it received, so it would report success on corrupted bytes.
157
111
 
158
112
  ```bash
159
- python3 {rebuildManagedSectionsHelper} {context_file} replace --content "{new_managed_section_text}"
113
+ python3 {rebuildManagedSectionsHelper} "{context_path}" replace < "{context_path}.skf-content"
160
114
  ```
161
115
 
162
- The helper handles marker location, between-marker swap, atomic temp-file + rename, and post-write verification (markers preserved, content outside markers byte-identical). It exits non-zero on any failure with a clear `stderr` reason.
163
-
164
- 9. **Verify (deferred to helper).** The `replace` action above performs verification internally. Treat any non-zero exit code as a per-file failure (next bullet). If the helper is missing entirely (no probe candidate exists), HALT (exit code 4, `halt_reason: "context-rebuild-failed"`) — the rewrite cannot proceed without the atomic helper.
116
+ Delete `{context_path}.skf-content` once the helper returns, whatever its exit code. It refuses a body that holds a marker of its own, and then leaves the file unchanged. It exits 0 only when the `status` of its JSON is `"ok"`. On a non-zero exit, bind `{context_error}` ← `error`, or the helper's stderr when stdout holds no JSON.
165
117
 
166
- 10. **On per-file failure:** record the error against that context file and continue to the next entry. Do not halt — other context files should still be rebuilt.
118
+ 4. **On per-file failure:** record `{context_error}` against that context file, record the warning `context_rebuild_failed: {context_file}: {context_error}`, and continue to the next entry. Do not halt: other context files should still be rebuilt.
167
119
 
168
120
  **After the loop,** record `context_files_updated` as the list of files that were successfully rewritten, and `context_files_failed` as the list of any that failed.
169
121
 
@@ -175,84 +127,69 @@ Report: "**Rebuilt managed sections in:** {list of updated files}. {if any faile
175
127
 
176
128
  **If `drop_mode == "purge"`:**
177
129
 
178
- 1. Initialize `files_deleted = []` and `delete_failures = []` (paths whose deletion was attempted but did not succeed).
179
-
180
- 2. **Measure sizes before deleting anything.** Run the sizing helper once over `affected_directories` so each path's byte size is captured while it still exists:
130
+ 1. **Delete through the guarded delete.** Resolve `{skillInventoryHelper}` from `{skillInventoryProbeOrder}` (first existing path wins) and run it once over every path in `affected_directories`, with the skills folder and the forge folder as the only folders it may delete inside:
181
131
 
182
132
  ```bash
183
- uv run {dirSizesHelper} sizes {each path in affected_directories, space-separated}
133
+ uv run {skillInventoryHelper} guarded-delete --root "{skills_output_folder}" --root "{forge_data_folder}" {each path in affected_directories, quoted, space-separated}
184
134
  ```
185
135
 
186
- Keep each `result.paths[].bytes` as `path_bytes[{path}]`; a path reported `exists: false` is already gone. If the helper is unavailable, fall back to `du -sb` per existing path.
136
+ The helper removes any trailing `/`, and SKF never deletes through a link. Bind `files_deleted` ← `files_deleted`, `delete_failures` ← `delete_failures` (each `{path, error}`), `{bytes_freed}` ← `bytes_freed` and `{purge_status}` ← `purge_status`. An empty `affected_directories` (a manifest entry whose folders are already gone) deletes nothing and returns `success`.
187
137
 
188
- 3. For each directory path in `affected_directories`:
189
- a. Verify the path is inside either `{skills_output_folder}` or `{forge_data_folder}` (defense in depth against accidental deletion of unrelated paths)
190
- b. If the directory does not exist, record it as "(already absent)" and continue
191
- c. Delete the directory recursively
192
- d. Verify deletion succeeded (the path no longer exists)
193
- e. Append the path to `files_deleted`
138
+ When no candidate exists, delete nothing by hand: take the `failed` outcome below, with "the inventory helper that checks each delete is missing; re-install SKF" as the error of every path. On a non-zero exit, or no JSON on stdout, take the `failed` outcome too, with the helper's `error` (its stderr when stdout holds no JSON) as the error of every path.
194
139
 
195
- 4. **Version-level purge, single version:**
196
- - `{skills_output_folder}/{target_skill}/{version}/` is deleted, but `{skills_output_folder}/{target_skill}/` remains (it still contains other versions or the `active` symlink)
197
- - If the `active` symlink pointed to the just-deleted version, update or remove it. The version directory is already gone at this point, so a symlink problem never claims `delete-failed` — record it and continue (`verification_errors`) so the report surfaces the manual repair rather than masking a successful purge.
198
- - **Other non-deprecated versions remain** for `{target_skill}`: resolve `{new_active_version}` = the version the manifest now lists as `active_version` for `{target_skill}` (if that one is deprecated, the newest non-deprecated version in its `versions` map). Repoint `active` to it atomically through the shared helper rather than a hand-rolled `ln` — resolve `{updateActiveSymlinkHelper}` from `{updateActiveSymlinkProbeOrder}` (first existing path wins), then:
140
+ 2. **Version-level purge, single version:** if the `active` symlink pointed to the just-deleted version, update or remove it. The version folder is already gone, so a symlink problem never claims `delete-failed`: record the manual repair in `verification_errors`, record the warning `active_link_dangling: {skills_output_folder}/{target_skill}/active: {the manual repair}`, and continue. Read the versions the drop left through the inventory helper's `resolve`, never from the manifest by hand:
141
+ ```bash
142
+ uv run {skillInventoryHelper} resolve "{skills_output_folder}" --skill {target_skill} --forge-data-folder "{forge_data_folder}"
143
+ ```
144
+ When it exits non-zero, record the manual repair (point `active` at the newest version the manifest does not list as deprecated, or remove it when none is left) as above and continue.
145
+ - **Other non-deprecated versions remain** (`resolve.counts.non_deprecated` is above `0`): `{new_active_version}` ← `resolve.active_version`, the version the manifest lists as active, unless it is null or its `resolve.versions` entry has `status` `"deprecated"`; then `resolve.newest_non_deprecated`. Repoint `active` to it atomically through the shared helper rather than a hand-rolled `ln`: resolve `{updateActiveSymlinkHelper}` from `{updateActiveSymlinkProbeOrder}` (first existing path wins), then:
199
146
  ```bash
200
147
  python3 {updateActiveSymlinkHelper} update \
201
148
  --skill-group {skills_output_folder}/{target_skill} \
202
149
  --version {new_active_version}
203
150
  ```
204
- The helper does a temp-symlink + `os.replace` flip, so a concurrent reader never sees a missing `active`. Record a `mismatch`/`missing-target` exit (code 2), or a missing helper (no probe candidate), in `verification_errors` with the manual fix (`ln -sfn {new_active_version} {skills_output_folder}/{target_skill}/active`) and continue.
205
- - **No non-deprecated versions remain** (reachable only when dropping the sole surviving version, permitted in step 1 because no other non-deprecated versions existed): remove the now-dangling `active` symlink with a single atomic unlink of the link itself — `rm {skills_output_folder}/{target_skill}/active` (unlink removes only the symlink, never its target, and is atomic). A single unlink has one correct outcome and no intermediate state, so it stays in-prompt (the helper has no removal action).
206
-
207
- 5. **Skill-level purge:**
208
- - `{skills_output_folder}/{target_skill}/` and `{forge_data_folder}/{target_skill}/` are deleted in full — the `active` symlink disappears with the parent directory
151
+ In each manual fix below, `{atomicWriteHelper}` is the first existing path in `{atomicWriteProbeOrder}`, or its first entry when neither exists (the fix then works once SKF is re-installed); the same command works in Git Bash, PowerShell and WSL2. Record a `mismatch`/`missing-target` exit (code 2), or a missing helper (no probe candidate), as above with the manual fix `uv run {atomicWriteHelper} flip-link --link "{skills_output_folder}/{target_skill}/active" --target "{new_active_version}"`, and continue. Record a `busy` exit (code 2, another run was moving the link) the same way, with the manual fix "once the other run has finished, if `active` still names the dropped version, run `uv run {atomicWriteHelper} flip-link --link "{skills_output_folder}/{target_skill}/active" --target "{new_active_version}"`". Record any other non-zero exit, or no JSON on stdout (exit 1, or an exit 2 that prints no JSON: the helper could not run, could not move the link, or failed after moving it, and the last line it prints on stderr says why), the same way, with the manual fix "if `active` still names the dropped version, run `uv run {atomicWriteHelper} flip-link --link "{skills_output_folder}/{target_skill}/active" --target "{new_active_version}"`" followed by that line in parentheses, or by `(exit <code>)` when the helper prints nothing on stderr.
152
+ - **No non-deprecated versions remain** (`resolve.counts.non_deprecated` is `0`, reachable only when dropping the sole surviving version, permitted in step 1 because no other non-deprecated versions existed): remove the now-dangling `active` symlink with a single atomic unlink of the link itself: `rm {skills_output_folder}/{target_skill}/active` (unlink removes only the symlink, never its target). When it fails, record it as above with the manual fix `rm {skills_output_folder}/{target_skill}/active`.
209
153
 
210
- 6. Sum the sizes of the paths in `files_deleted` and format one human-readable label through the helper — do not add or round in-prompt:
154
+ 3. Format the size of the deleted folders through the sizing helper; do not add or round in-prompt:
211
155
 
212
156
  ```bash
213
- uv run {dirSizesHelper} humanize {path_bytes[p] for each p in files_deleted, space-separated}
157
+ uv run {dirSizesHelper} humanize {bytes_freed}
214
158
  ```
215
159
 
216
160
  Store `result.total_human` as `disk_freed` (e.g. `"4.2 MB"`; `"0 B"` when nothing was deleted).
217
161
 
218
- **On deletion error (per path):**
162
+ **Classify the deletion outcome from `{purge_status}`:**
219
163
 
220
- - Append the path and its error message to `delete_failures`
221
- - Continue attempting the remaining paths — a partial purge is still better than no purge
222
- - Report all failures at the end of this section
223
-
224
- **After the loop — classify the deletion outcome.** Let `attempted` be the number of paths in `affected_directories` that existed on disk (i.e. were not recorded as "(already absent)"):
225
-
226
- - **Full purge failure** — `attempted > 0` AND every attempted path is in `delete_failures` (nothing was deleted): the purge accomplished none of its destructive intent, so it must NOT report success. HALT (exit code 4, `halt_reason: "delete-failed"`): "**Purge failed** — none of the {attempted} target director(ies) could be deleted: {list each path with its error}. The manifest and context files were already updated in sections 2–3; the on-disk files remain and can be removed manually (`rm -rf {path}`)." In headless mode, emit the error envelope per `{headlessContract}` with the resolved `skill`, `drop_mode`, `versions_affected`, `files_deleted: []`, and `manifest_updated` from section 2. Do not proceed to section 5.
227
- - **Partial purge failure** — `delete_failures` is non-empty but at least one path was deleted: keep record-and-continue. Set `purge_status = "partial"` so step 3's on-disk result record reflects it (the `output-contract-schema.md` `status` enum supports `"partial"`); proceed to section 5. The headless single-line envelope has no `"partial"` value in its enum, so it stays `"success"` while `context_files_failed`/`verification_errors`/the report surface the unfreed paths.
228
- - **No failures** — `delete_failures` is empty: set `purge_status = "success"` and proceed to section 5.
164
+ - **`failed`**: at least one path was attempted and none was deleted, so the purge accomplished none of its destructive intent and must NOT report success. HALT (exit code 4, `halt_reason: "delete-failed"`, phase `execute:delete`): "**Purge failed:** none of the target folders could be deleted: {list each `delete_failures` path with its error}. The manifest and context files were already updated in sections 2–3; the on-disk files remain and can be removed by hand (`rm -rf {path}`)." The §1 halt envelope carries the resolved `skill`, `drop_mode` and `versions_affected`, `files_deleted: []`, and `manifest_updated` from section 2. Do not proceed to section 5.
165
+ - **`partial`**: `delete_failures` is non-empty but at least one path was deleted. Keep record-and-continue: `purge_status = "partial"` lets step 3's on-disk result record reflect it (the `output-contract-schema.md` `status` enum supports `"partial"`). Record the warning `delete_failed: {path}: {error}` for each `delete_failures` entry, then proceed to section 5.
166
+ - **`success`**: nothing failed (a path already gone is not a failure). Proceed to section 5.
229
167
 
230
168
  ### 5. Verify Final State
231
169
 
232
170
  Run these verification checks:
233
171
 
234
- 1. **Manifest check:** Re-read `{skills_output_folder}/.export-manifest.json` and confirm:
235
- - Version-level drop: `exports.{target_skill}.versions.{version}.status == "deprecated"`
236
- - Skill-level drop: `exports.{target_skill}` is absent
172
+ 1. **Manifest check** (skip it when `target_in_manifest == false`: section 2 changed no entry). Read the entry through `{manifestOpsHelper}`:
173
+
174
+ ```bash
175
+ python3 {manifestOpsHelper} {skills_output_folder} get {target_skill}
176
+ ```
237
177
 
238
- 2. **Context files check:** For each file in `context_files_updated`, spot-check that the dropped skill/version is no longer referenced between the markers.
178
+ Version-level drop: `entry.versions.{version}.status` is `"deprecated"` for each version in `target_versions`. Skill-level deprecate: every version in `entry.versions` has `status` `"deprecated"`. Skill-level purge: `status` is `"not_found"`. A failure's manual fix: correct the `{target_skill}` entry of `{skills_output_folder}/.export-manifest.json` by hand.
239
179
 
240
- 3. **Purge check (purge mode only):** For each path in `files_deleted`, confirm it no longer exists on disk.
180
+ 2. **Context files check** (skip it when `context_files_updated` is empty): list the rows the rebuilt files still hold for the dropped skill, by name and version only:
241
181
 
242
- If any verification fails, record the specific failure in `verification_errors` but do not halt — proceed to step 3 so the report can surface what succeeded and what needs manual attention.
182
+ ```bash
183
+ uv run {dropRosterHelper} rows --skill {target_skill} [--version {version}] {each file in context_files_updated, quoted}
184
+ ```
243
185
 
244
- ### 6. Store Results in Context
186
+ Pass `--version` for a version-level drop. Each `rows` entry fails the check, as does each `unchecked` entry (a file whose rows could not be read); the manual fix for both is to re-run `[EX] Export Skill`, which rebuilds the file. When the call exits non-zero, the check fails for every file in `context_files_updated`, with the helper's `error` (its stderr when stdout holds no JSON).
187
+
188
+ If any verification fails, record it in `verification_errors` with its manual fix, record the warning `verification_failed: {what failed}: {its manual fix}`, and do not halt: proceed to step 3 so the report shows what succeeded and what needs manual attention.
245
189
 
246
- Store the following for step 3:
190
+ ### 6. Store Results in Context
247
191
 
248
- - `files_deleted` — list of directory paths actually deleted (purge mode) or `[]` (soft drop)
249
- - `disk_freed` — human-readable size (purge mode) or `"N/A (soft drop)"`
250
- - `delete_failures` — list of `{path, error}` for paths whose deletion was attempted but failed (empty if none; a *full* purge failure already HALTed in section 4 and never reaches this step)
251
- - `purge_status` — `"success"`, `"partial"` (some paths failed to delete), or `"success"` for soft drops; step 3 maps this to the on-disk result record's `status` field
252
- - `manifest_updated` — boolean (true if section 2 succeeded)
253
- - `context_files_updated` — list of successfully rebuilt files
254
- - `context_files_failed` — list of files that failed to rebuild (empty if none)
255
- - `verification_errors` — list of verification failures (empty if none)
192
+ Step 3 reads what this step bound where it ran: `files_deleted`, `disk_freed`, `delete_failures`, `purge_status`, `manifest_updated`, `context_files_updated`, `context_files_failed` and `verification_errors`, with `forge_left_in_place` carried from step 1.
256
193
 
257
194
  ### 7. Load Next Step
258
195
 
@@ -10,3 +10,5 @@ nextStepFile: 'shared/health-check.md'
10
10
  # Step 4: Workflow Health Check
11
11
 
12
12
  This is the terminal step of drop-skill. Load `{nextStepFile}`, read it fully, then execute it — do nothing else here (no user-facing reports, file writes, or result contracts; those were step 3). After the shared health check completes, the workflow is done.
13
+
14
+ In a headless run report.md bound `{result_envelope_line}` to the `SKF_DROP_SKILL_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.
@@ -0,0 +1,64 @@
1
+ # Invocation Contract: skf-drop-skill
2
+
3
+ The arguments, gates, exit codes and headless result envelope of `skf-drop-skill`. Interactive runs do not need this file; headless automators and pipelines read it, and the SKILL.md On-Activation HALTs follow its Halt Envelope section.
4
+
5
+ ## Invocation Contract
6
+
7
+ | Aspect | Detail |
8
+ |--------|--------|
9
+ | **Inputs** | skill_name [required], mode (deprecate/purge) [required], version (`all` or one version) [required for a skill in the manifest; a draft takes only `all`]. An argument supplied at invocation answers its step 1 prompt in either mode, so that prompt is not shown |
10
+ | **Flags** | `--headless` / `-H` (auto-resolve all gates); `--dry-run` (run selection and display the §10 confirmation block, then exit with `status="dry-run"`: no manifest mutation, no file deletion) |
11
+ | **Gates** | step 1: Input Gate [use args] (§4 skill), Scope Gate [use args] (§6 version), Mode Gate [use args] (§8 mode), Confirm Gate [Y] |
12
+ | **Outputs** | Updated manifest, rebuilt context files, (purge: deleted directories), `drop-skill-result-{YYYYMMDD-HHmmss}.json` (UTC) and `drop-skill-result-latest.json` in `{skills_output_folder}` (none for a dry run or a HALT); a run folder under `{project-root}/_bmad-output/.skf-run/`, kept only after a HALT, except an interactive HALT in step 1 or 2, which deletes it |
13
+ | **Headless** | Gates auto-resolve with their default action (SKILL.md Workflow Rules); select.md §6 and §8 halt when the `version` or `mode` argument a headless run must pass is missing. Any non-empty `forbid_purge_in_headless`, set under `[workflow]` in a team or personal override (never in the bundled `customize.toml`, which every update overwrites), stops a headless `mode=purge` at On-Activation §4 (exit code 6, `halt_reason: "headless-purge-forbidden"`) when the customization resolver runs. When it cannot, including an override file that fails to parse, the run warns `customization_resolver_unavailable` and the bundled empty value leaves the guard off. A key written without the `[workflow]` line is ignored with no warning, and the guard stays off. |
14
+ | **Exit codes** | See "Exit Codes" below |
15
+
16
+ ## Exit Codes
17
+
18
+ Every hard HALT exits with a stable code; the step files name the exact `halt_reason` and phase at each HALT site, and the envelope below carries both.
19
+
20
+ | Code | Meaning | Raised by (class) |
21
+ | ---- | -------------------- | ----------------- |
22
+ | 0 | success | step 4 (terminal) |
23
+ | 2 | input-missing / input-invalid | step 1 input gates: a missing or unmatched `skill_name`, `version` or `--mode` value (§4 / §6 / §8), or a draft skill given a specific `version` or `mode=deprecate` |
24
+ | 3 | resolution-failure | step 1 §2 corrupt manifest, §3 nothing to drop |
25
+ | 4 | write-failure | On-Activation §4, or step 1 §2 when the roster helper cannot run (`write-failed`); step 1 §9b, before any change (`context-rebuild-failed`); step 2 manifest write, in every mode (`manifest-write-failed`), or a purge that deleted nothing (`delete-failed`) |
26
+ | 5 | state-conflict | step 1 §7 active-version guard; §3, §4 or §8b ownership guard (`not-skf-output`) |
27
+ | 6 | user-cancelled | any interactive cancel or confirm-gate `[N]`; On-Activation headless-purge guard |
28
+
29
+ ## Result Contract (Headless)
30
+
31
+ The single-line JSON contract every headless run of skf-drop-skill prints. The shared emitter, `shared/scripts/skf-emit-result-envelope.py`, builds each line from the payload the emitting step hands it and checks it against `shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json`, so no stage types an envelope and a HALT prints the right shape whatever else is still in context.
32
+
33
+ When `{headless_mode}` is true, step 3 prints this envelope on **stdout** and the shared health check (step 4) displays it as the run's last line, a `--dry-run` prints it on **stdout** at the step 1 confirmation gate and displays it last there, and every HARD HALT prints it on **stderr** with `status: "error"`:
34
+
35
+ ```
36
+ SKF_DROP_SKILL_RESULT_JSON: {"status":"success|error|dry-run","skill":"…|null","drop_mode":"…|null","versions_affected":[],"files_deleted":[],"would_delete":[],"forge_left_in_place":null,"manifest_updated":false,"result_path":null,"exit_code":0,"halt_reason":null,"error":null}
37
+ ```
38
+
39
+ Field rules:
40
+
41
+ - `status`: `"success"` on the terminal happy path, `"dry-run"` when `--dry-run` was set and the workflow stopped at the confirmation gate before any change, `"error"` on any HALT.
42
+ - `halt_reason`: on success and dry-run it is `null`; otherwise one of `"input-missing"`, `"input-invalid"`, `"manifest-corrupt"`, `"nothing-to-drop"`, `"active-version-guard-refused"`, `"not-skf-output"`, `"headless-purge-forbidden"`, `"manifest-write-failed"`, `"context-rebuild-failed"`, `"delete-failed"`, `"write-failed"`, `"user-cancelled"`.
43
+ - `exit_code`: the emitter derives it from `halt_reason`, per the Exit Codes table above (`0` on success and dry-run).
44
+ - `skill`, `drop_mode`, `versions_affected`, `files_deleted`, `manifest_updated`: the values the emit site knows; a key not resolved yet takes the default shown in the template (`null`, `[]` or `false`). `versions_affected` is a list holding the one version dropped, or the string `"all"` for the whole skill.
45
+ - `would_delete`: in a dry run of a purge, the folders the purge would delete (the step 1 purge check's `affected_directories`), so an automator can check the blast radius before it runs the drop; `[]` otherwise, and in a dry run of a deprecate.
46
+ - `forge_left_in_place`: the forge folder a purge leaves in place (or, in a dry run, would leave) because SKF did not generate it; `null` otherwise.
47
+ - `result_path`: the per-run result file step 3 wrote, `{skills_output_folder}/drop-skill-result-{YYYYMMDD-HHmmss}.json` (UTC), stamped by the emitter; `null` for a dry run and a HALT, which write no result file.
48
+ - `error`: `null` on success and dry-run; on a HALT, `{"phase", "reason"}`, plus `path` when the halt names one: the step and gate that halted, and the halt message.
49
+ - `warnings`: present only when the run recorded at least one. A run that reports `success` can still carry the degraded outcomes step 2 records, which the report also shows: `context_rebuild_failed: <context file>: <error>` (a context file it could not rebuild), `active_link_dangling: <skill folder>/active: <manual fix>` (an `active` link a version purge left pointing at no version), `delete_failed: <path>: <error>` (a folder a partial purge could not delete) and `verification_failed: <what failed>: <manual fix>` (a final-state check that failed). Any run can carry `result_file_write_failed: <path>: <reason>` when the emitter could not write a result file, and `customization_resolver_unavailable: <reason>` when On Activation records that the customization resolver could not run, so the overrides under `{project-root}/_bmad/custom/` were not applied.
50
+
51
+ Each emit site supplies its `halt_reason`, `exit_code` and phase, and whichever discriminating fields it knows; the emitter fills in the rest.
52
+
53
+ ## Halt Envelope
54
+
55
+ Every HALT names its exit code, `halt_reason` and phase where it stops. In headless mode it prints its envelope before it stops: it stages `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "exit_code": <code>}`, plus `"path"` when the halt names one and the envelope fields the site names, runs the first command below and displays the line it prints verbatim. The SKILL.md On-Activation run-folder halt has no folder to stage in, so it hands that payload, on one line, to the second command. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. The emitter gives every field the payload leaves out its default (`null`, `[]` or `false`) and checks `exit_code` against `halt_reason`. When the helper is missing, exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing; in step 1 or 2 it then deletes the run folder.
56
+
57
+ ```bash
58
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-drop-skill --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
59
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-drop-skill --target stderr <<'SKF_DROP_HALT'
60
+ {"phase": "on-activation:run-folder", "reason": "<the halt message>", "halt_reason": "write-failed", "exit_code": 4}
61
+ SKF_DROP_HALT
62
+ ```
63
+
64
+ SKILL.md On-Activation §4 binds `{emitEnvelopeHelper}` and `{run_dir}`, and its HALTs follow this section. select.md and execute.md state the same rule in their section 1, so the HALTs of a step never depend on this file.
@@ -1,14 +1,10 @@
1
1
  ---
2
2
  nextStepFile: 'health-check.md'
3
- # §1 resolves `{manifestOpsHelper}` from this order (installed SKF module path
4
- # first, src/ dev-checkout fallback) to read the target skill's remaining
5
- # versions — `get` for the versions+status map and `affected-versions` for the
6
- # numeric semver-descending order — instead of re-parsing the manifest by hand.
7
- # A read, not an atomicity-critical write: if neither path resolves, §1 reads
8
- # the manifest in-prompt.
9
- manifestOpsProbeOrder:
10
- - '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
11
- - '{project-root}/src/shared/scripts/skf-manifest-ops.py'
3
+ # SKILL.md On-Activation §4 binds `{manifestOpsHelper}`, which §1 reads the
4
+ # skill's remaining versions through (`get` for the versions and their status,
5
+ # `affected-versions` for the numeric semver-descending order), and
6
+ # `{emitEnvelopeHelper}` with `{run_dir}`: the emitter stamps the time and the
7
+ # result file names, so this step types neither.
12
8
  ---
13
9
 
14
10
  <!-- Config: communicate in {communication_language}. Render the report block in {document_output_language}. -->
@@ -22,20 +18,19 @@ Present a clear, final summary of what the drop workflow changed — manifest st
22
18
  ## Rules
23
19
 
24
20
  - Focus only on reporting results stored in context by step 2 — do not re-execute any part of the drop
25
- - Do not hide verification errors or failed context file rebuilds
26
- - Chains to the local health-check step via `{nextStepFile}` after completion (see §3)
21
+ - Do not hide verification errors, failed context file rebuilds or folders a purge could not delete
27
22
 
28
23
  ## MANDATORY SEQUENCE
29
24
 
30
25
  ### 1. Determine Remaining Versions
31
26
 
32
- **If `is_skill_level == true`:**
27
+ **If `is_skill_level == true` and `drop_mode == "purge"`:**
33
28
 
34
29
  Set `remaining_versions_display = "(skill fully removed)"`.
35
30
 
36
- **If `is_skill_level == false`:**
31
+ **Otherwise** (one version, or a whole-skill deprecate, which keeps every version in the manifest as `deprecated`):
37
32
 
38
- **Resolve `{manifestOpsHelper}`** ← first existing path in `{manifestOpsProbeOrder}`, then read the target skill's remaining versions through it rather than re-parsing the manifest by hand:
33
+ Read the target skill's remaining versions through the `{manifestOpsHelper}` On-Activation §4 resolved, rather than re-parsing the manifest by hand:
39
34
 
40
35
  ```bash
41
36
  python3 {manifestOpsHelper} {skills_output_folder} get {target_skill}
@@ -50,8 +45,6 @@ python3 {manifestOpsHelper} {skills_output_folder} affected-versions {target_ski
50
45
  - 0.1.0 (deprecated)
51
46
  ```
52
47
 
53
- **If neither `{manifestOpsProbeOrder}` candidate resolves:** read `exports.{target_skill}.versions` from `{skills_output_folder}/.export-manifest.json` in-prompt, list each remaining version with its `status` (active marked `*`), ordering newest-first by comparing version components numerically.
54
-
55
48
  ### 2. Render the Report
56
49
 
57
50
  Display the following block, filling in values from context:
@@ -71,6 +64,8 @@ Changes:
71
64
  {if drop_mode == "purge":}
72
65
  - Files deleted: {list from files_deleted, or "(none — nothing on disk)"}
73
66
  - Disk space freed: {disk_freed}
67
+ {if delete_failures is non-empty:}- Not deleted: {each delete_failures path with its error} (remove by hand: `rm -rf {path}`)
68
+ {if forge_left_in_place:}- Left in place (not SKF output): {forge_left_in_place}
74
69
 
75
70
  Remaining versions for {target_skill}:
76
71
  {remaining_versions_display}
@@ -84,40 +79,61 @@ restore the managed section entry.
84
79
  {if verification_errors is non-empty:}
85
80
  **Verification warnings:**
86
81
  {list each verification error}
87
- These require manual review — see the error-handling guidance in step 2.
82
+ Each item above names its manual fix.
88
83
  ```
89
84
 
90
85
  ### Result Contract
91
86
 
92
- Write the result contract per `shared/references/output-contract-schema.md`: the per-run record at `{skills_output_folder}/drop-skill-result-{YYYYMMDD-HHmmss}.json` (UTC timestamp, resolution to seconds) and a copy at `{skills_output_folder}/drop-skill-result-latest.json` (stable path for pipeline consumers — copy, not symlink). Include all purged file paths in `outputs`; include `target_skill`, `drop_mode`, `versions_affected`, and a `headless_provenance` object in `summary`.
93
-
94
- `headless_provenance` persists the §8/§10 decision trail from step 1 so an unattended run's auto-decisions survive in the durable record, not only in the transient log — a consumer can tell an operator-confirmed drop from a headless auto-confirmed one without re-deriving it:
87
+ The shared emitter writes the result contract (`shared/references/output-contract-schema.md`) and prints the envelope; this step stages their content. Write `{run_dir}/result-context.json`:
95
88
 
96
89
  ```json
97
- "headless_provenance": {"headless": {headless_mode}, "mode_source": "{mode_source}", "confirm": "{confirm_source}"}
90
+ {
91
+ "status": "success",
92
+ "skill": "{target_skill}",
93
+ "drop_mode": "{drop_mode}",
94
+ "versions_affected": {target_versions},
95
+ "files_deleted": {files_deleted},
96
+ "forge_left_in_place": {forge_left_in_place},
97
+ "manifest_updated": {manifest_updated},
98
+ "result_contract": {
99
+ "skill": "skf-drop-skill",
100
+ "status": "{record_status}",
101
+ "outputs": [{"type": "skill", "path": "{each path in files_deleted}"}],
102
+ "summary": {
103
+ "target_skill": "{target_skill}",
104
+ "drop_mode": "{drop_mode}",
105
+ "versions_affected": {target_versions},
106
+ "forge_left_in_place": {forge_left_in_place},
107
+ "headless_provenance": {"headless": {headless_mode}, "mode_source": "{mode_source}", "confirm": "{confirm_source}"}
108
+ }
109
+ }
110
+ }
98
111
  ```
99
112
 
100
- where `{headless_mode}` is the resolved boolean, `{mode_source}` is the step-1 §8 value (`"--mode argument"` / `"customize.toml.workflow.default_mode"` / `"interactive-prompt"` / `"draft-skill-forced-purge"`), and `{confirm_source}` is the step-1 §10 value (`"headless-auto"` / `"user-explicit"`).
101
-
102
- Set the record's `status` from step 2's `purge_status`: `"partial"` when some (but not all) purge-mode directories failed to delete — surface the failing paths from `delete_failures` in `summary.delete_failures` — otherwise `"success"`. A *full* purge failure never reaches this step: step 2 §4 HALTs with `halt_reason: "delete-failed"` and the error-envelope path below handles it.
113
+ - `{target_versions}` is a JSON array (e.g. `["0.5.0"]`) or the string `"all"`; `{files_deleted}` a JSON array of absolute paths (`[]` in deprecate mode, and so is `outputs`); `{forge_left_in_place}` the path step 2 carried, or `null` when none and in deprecate mode; `{manifest_updated}` the boolean from step 2.
114
+ - `{record_status}` is `"partial"` when some (but not all) purge folders failed to delete (step 2's `purge_status`); then also put step 2's `delete_failures` in `summary.delete_failures`. Otherwise it is `"success"`. A full purge failure and a failed manifest write never reach this step: step 2 HALTs with `halt_reason: "delete-failed"` or `"manifest-write-failed"`.
115
+ - `headless_provenance` persists the §8/§10 decision trail from step 1, so an unattended run's auto-decisions survive in the durable record and a consumer can tell an operator-confirmed drop from a headless auto-confirmed one: `{headless_mode}` is the resolved boolean, and `{mode_source}` and `{confirm_source}` the values step 1 set.
116
+ - The payload carries no `warnings`: the emitter adds the ones step 2 recorded, so a run whose envelope reads `success` still names what needs a manual fix.
103
117
 
104
- When `{headless_mode}` is true, also emit the single-line envelope on **stdout** before chaining to step 4 (the full shape and field rules are in `references/headless-contract.md`):
118
+ Then run, in every mode:
105
119
 
106
- ```
107
- SKF_DROP_SKILL_RESULT_JSON: {"status":"success","skill":"{target_skill}","drop_mode":"{drop_mode}","versions_affected":{target_versions},"files_deleted":{files_deleted},"manifest_updated":{manifest_updated},"exit_code":0,"halt_reason":null}
120
+ ```bash
121
+ uv run {emitEnvelopeHelper} emit --workflow skf-drop-skill --run-dir "{run_dir}" --result-dir "{skills_output_folder}" < "{run_dir}/result-context.json"
108
122
  ```
109
123
 
110
- Substitute `{target_versions}` as a JSON array (e.g. `["0.5.0"]`) or the literal string `"all"`; substitute `{files_deleted}` as a JSON array of absolute paths (`[]` in soft-drop mode); `manifest_updated` is the boolean from step 2's context.
124
+ The emitter stamps the UTC time, the run id, the run's warnings and the auto-decisions select.md recorded (`headless_decisions`) into the record, writes it as `{skills_output_folder}/drop-skill-result-{YYYYMMDD-HHmmss}.json`, copies it to `{skills_output_folder}/drop-skill-result-latest.json` (the stable path pipelines read), and prints the `SKF_DROP_SKILL_RESULT_JSON:` line on stdout (field rules in `references/invocation-contract.md`). Bind `{result_json_path}` ← that line's `result_path`: null when the record could not be written, and the line's `warnings` then say why. When `{headless_mode}` is true, bind `{result_envelope_line}` ← the line and do not display it here: the shared health check displays it verbatim as the run's last line, so a `claude -p` caller reads the envelope as the final message. If the helper exits non-zero, correct `result-context.json` from the `message` of its stderr JSON and run it once more; if it fails again, leave `{result_envelope_line}` empty, display "The drop result record could not be written: {message}" and go on, since the drop itself is complete.
111
125
 
112
126
  ### Post-drop hook (optional)
113
127
 
114
- If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it once the result contract above is finalized:
128
+ If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`) and `{result_json_path}` is not null, invoke it once the emitter has written the record:
115
129
 
116
130
  ```bash
117
131
  {onCompleteCommand} --result-path={result_json_path}
118
132
  ```
119
133
 
120
- where `{result_json_path}` is the per-run record written above (`{skills_output_folder}/drop-skill-result-{YYYYMMDD-HHmmss}.json`). Log success or failure to `workflow_warnings[]` — never fail the workflow on a hook error; the drop has already completed and may be irreversible. When `{onCompleteCommand}` is empty (bundled default), skip the invocation entirely.
134
+ When it exits non-zero, display "on_complete hook failed (exit {code}): {its first stderr line}" as a line of its own, and never fail the workflow on a hook error: the drop has already completed and may be irreversible. When `{onCompleteCommand}` is empty (bundled default), skip the invocation entirely.
135
+
136
+ Whether or not a hook ran, then delete the run folder, whose payload the emitter has read: `rm -rf "{run_dir}"`.
121
137
 
122
138
  ### 3. Chain to Health Check
123
139