bmad-module-skill-forge 2.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +49 -49
  3. package/docs/404.md +10 -8
  4. package/docs/_data/pinned.yaml +22 -13
  5. package/docs/_internal/STABILITY.md +58 -35
  6. package/docs/agents.md +39 -53
  7. package/docs/architecture.md +65 -51
  8. package/docs/bmad-synergy.md +53 -51
  9. package/docs/campaign.md +127 -75
  10. package/docs/concepts.md +34 -28
  11. package/docs/examples.md +70 -68
  12. package/docs/forge-auto.md +34 -31
  13. package/docs/getting-started.md +118 -57
  14. package/docs/how-it-works.md +25 -21
  15. package/docs/index.md +15 -15
  16. package/docs/skill-model.md +70 -59
  17. package/docs/troubleshooting.md +135 -18
  18. package/docs/verifying-a-skill.md +83 -57
  19. package/docs/why-skf.md +24 -22
  20. package/docs/workflows.md +274 -110
  21. package/package.json +18 -10
  22. package/src/forger/preferences.yaml +8 -1
  23. package/src/knowledge/agentskills-spec.md +2 -0
  24. package/src/knowledge/ccc-bridge.md +40 -24
  25. package/src/knowledge/confidence-tiers.md +9 -5
  26. package/src/knowledge/overview.md +3 -3
  27. package/src/knowledge/progressive-capability.md +8 -8
  28. package/src/knowledge/provenance-tracking.md +9 -5
  29. package/src/knowledge/qmd-registry.md +2 -2
  30. package/src/knowledge/skf-knowledge-index.csv +2 -2
  31. package/src/knowledge/skill-lifecycle.md +18 -11
  32. package/src/knowledge/split-body-strategy.md +0 -3
  33. package/src/knowledge/tool-resolution.md +4 -2
  34. package/src/knowledge/version-paths.md +101 -15
  35. package/src/module-help.csv +3 -3
  36. package/src/module.yaml +10 -0
  37. package/src/shared/data/ast-grep-recipes.yaml +608 -0
  38. package/src/shared/data/ide-context-files.json +30 -0
  39. package/src/shared/health-check.md +32 -12
  40. package/src/shared/references/description-guard-protocol.md +12 -9
  41. package/src/shared/references/feasibility-report-schema.md +19 -13
  42. package/src/shared/references/headless-gate-convention.md +55 -3
  43. package/src/shared/references/output-contract-schema.md +19 -8
  44. package/src/shared/references/pipeline-contracts.md +94 -29
  45. package/src/shared/references/tessl-review.md +77 -0
  46. package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
  47. package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
  48. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
  49. package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
  50. package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
  51. package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
  52. package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
  53. package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
  54. package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
  55. package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
  56. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
  57. package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
  58. package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
  59. package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
  60. package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
  61. package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
  62. package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
  63. package/src/shared/scripts/skf-atomic-write.py +30 -14
  64. package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
  65. package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
  66. package/src/shared/scripts/skf-check-unit-records.py +184 -0
  67. package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
  68. package/src/shared/scripts/skf-classify-changed-files.py +694 -0
  69. package/src/shared/scripts/skf-comention-pairs.py +1333 -123
  70. package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
  71. package/src/shared/scripts/skf-count-imports.py +1867 -0
  72. package/src/shared/scripts/skf-count-tokens.py +1 -1
  73. package/src/shared/scripts/skf-derive-assembly-shape.py +3 -2
  74. package/src/shared/scripts/skf-description-guard.py +99 -11
  75. package/src/shared/scripts/skf-detect-docs.py +694 -2
  76. package/src/shared/scripts/skf-detect-language.py +486 -107
  77. package/src/shared/scripts/skf-detect-registry.py +887 -0
  78. package/src/shared/scripts/skf-detect-scripts-assets.py +1 -1
  79. package/src/shared/scripts/skf-detect-tools.py +209 -27
  80. package/src/shared/scripts/skf-detect-workspaces.py +506 -30
  81. package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
  82. package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
  83. package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
  84. package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
  85. package/src/shared/scripts/skf-extract-public-api.py +4084 -85
  86. package/src/shared/scripts/skf-extraction-inventory.py +746 -0
  87. package/src/shared/scripts/skf-extraction-snapshot.py +539 -0
  88. package/src/shared/scripts/skf-fetch-temporal.py +674 -0
  89. package/src/shared/scripts/skf-find-cycles.py +97 -29
  90. package/src/shared/scripts/skf-find-test-report.py +476 -0
  91. package/src/shared/scripts/skf-forge-tier-rw.py +444 -203
  92. package/src/shared/scripts/skf-github-fetch.py +365 -0
  93. package/src/shared/scripts/skf-github-probe.py +661 -0
  94. package/src/shared/scripts/skf-hash-content.py +166 -12
  95. package/src/shared/scripts/skf-language-corpora.py +1 -1
  96. package/src/shared/scripts/skf-load-provenance.py +159 -27
  97. package/src/shared/scripts/skf-manifest-ops.py +1 -1
  98. package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
  99. package/src/shared/scripts/skf-merge-doc-urls.py +1 -1
  100. package/src/shared/scripts/skf-names-present.py +221 -0
  101. package/src/shared/scripts/skf-pair-intersect.py +119 -28
  102. package/src/shared/scripts/skf-parse-gaps.py +1384 -0
  103. package/src/shared/scripts/skf-preapply.py +1 -1
  104. package/src/shared/scripts/skf-preflight.py +161 -27
  105. package/src/shared/scripts/skf-promote-staged.py +430 -0
  106. package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
  107. package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
  108. package/src/shared/scripts/skf-quick-batch.py +774 -0
  109. package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
  110. package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
  111. package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
  112. package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
  113. package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
  114. package/src/shared/scripts/skf-resolve-authoritative-files.py +128 -13
  115. package/src/shared/scripts/skf-resolve-package.py +523 -106
  116. package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
  117. package/src/shared/scripts/skf-run-lock.py +577 -0
  118. package/src/shared/scripts/skf-scan-manifests.py +998 -57
  119. package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
  120. package/src/shared/scripts/skf-severity-classify.py +712 -108
  121. package/src/shared/scripts/skf-shape-detect.py +298 -204
  122. package/src/shared/scripts/skf-shard-body.py +9 -8
  123. package/src/shared/scripts/skf-skill-inventory.py +2412 -89
  124. package/src/shared/scripts/skf-skills-module.py +472 -0
  125. package/src/shared/scripts/skf-source-tree.py +1796 -0
  126. package/src/shared/scripts/skf-structural-diff.py +670 -119
  127. package/src/shared/scripts/skf-tessl-review.py +706 -0
  128. package/src/shared/scripts/skf-update-active-symlink.py +5 -5
  129. package/src/shared/scripts/skf-update-run-state.py +624 -0
  130. package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
  131. package/src/shared/scripts/skf-validate-brief-schema.py +4 -1
  132. package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
  133. package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
  134. package/src/shared/scripts/skf-validate-output.py +712 -64
  135. package/src/shared/scripts/skf-validate-pins.py +1 -1
  136. package/src/shared/scripts/skf-verify-no-trace.py +116 -10
  137. package/src/shared/scripts/skf-verify-provenance-completeness.py +2919 -54
  138. package/src/shared/scripts/skf-write-skill-brief.py +367 -19
  139. package/src/shared/tool-requirements.yaml +201 -0
  140. package/src/skf-analyze-source/SKILL.md +41 -36
  141. package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
  142. package/src/skf-analyze-source/customize.toml +36 -24
  143. package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
  144. package/src/skf-analyze-source/references/continue.md +2 -13
  145. package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
  146. package/src/skf-analyze-source/references/generate-briefs.md +121 -55
  147. package/src/skf-analyze-source/references/headless-contract.md +42 -15
  148. package/src/skf-analyze-source/references/identify-units.md +96 -110
  149. package/src/skf-analyze-source/references/init.md +73 -71
  150. package/src/skf-analyze-source/references/map-and-detect.md +123 -105
  151. package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
  152. package/src/skf-analyze-source/references/recommend.md +51 -70
  153. package/src/skf-analyze-source/references/scan-project.md +60 -84
  154. package/src/skf-analyze-source/references/scan-root.md +39 -0
  155. package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
  156. package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
  157. package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
  158. package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
  159. package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
  160. package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
  161. package/src/skf-audit-skill/SKILL.md +43 -51
  162. package/src/skf-audit-skill/assets/drift-report-template.md +27 -3
  163. package/src/skf-audit-skill/customize.toml +41 -20
  164. package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
  165. package/src/skf-audit-skill/references/doc-drift.md +141 -0
  166. package/src/skf-audit-skill/references/headless-contract.md +29 -0
  167. package/src/skf-audit-skill/references/init.md +148 -210
  168. package/src/skf-audit-skill/references/re-index.md +96 -108
  169. package/src/skf-audit-skill/references/report.md +96 -33
  170. package/src/skf-audit-skill/references/semantic-diff.md +27 -18
  171. package/src/skf-audit-skill/references/severity-classify.md +79 -65
  172. package/src/skf-audit-skill/references/severity-rules.md +15 -6
  173. package/src/skf-audit-skill/references/structural-diff.md +75 -90
  174. package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
  175. package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
  176. package/src/skf-brief-skill/SKILL.md +36 -25
  177. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  178. package/src/skf-brief-skill/assets/scope-templates.md +17 -24
  179. package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
  180. package/src/skf-brief-skill/customize.toml +48 -20
  181. package/src/skf-brief-skill/references/analyze-target.md +142 -125
  182. package/src/skf-brief-skill/references/confirm-brief.md +10 -8
  183. package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
  184. package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
  185. package/src/skf-brief-skill/references/gather-intent.md +87 -182
  186. package/src/skf-brief-skill/references/headless-args.md +84 -16
  187. package/src/skf-brief-skill/references/health-check.md +1 -1
  188. package/src/skf-brief-skill/references/invocation-contract.md +18 -13
  189. package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
  190. package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
  191. package/src/skf-brief-skill/references/scope-definition.md +123 -47
  192. package/src/skf-brief-skill/references/step-auto-brief.md +24 -80
  193. package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
  194. package/src/skf-brief-skill/references/version-resolution.md +3 -2
  195. package/src/skf-brief-skill/references/write-brief.md +66 -57
  196. package/src/skf-campaign/SKILL.md +31 -34
  197. package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
  198. package/src/skf-campaign/customize.toml +47 -16
  199. package/src/skf-campaign/references/campaign-contracts.md +39 -29
  200. package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
  201. package/src/skf-campaign/references/health-check.md +7 -16
  202. package/src/skf-campaign/references/step-01-setup.md +46 -84
  203. package/src/skf-campaign/references/step-02-strategy.md +31 -30
  204. package/src/skf-campaign/references/step-03-pins.md +26 -18
  205. package/src/skf-campaign/references/step-04-provenance.md +24 -23
  206. package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
  207. package/src/skf-campaign/references/step-06-batch.md +41 -34
  208. package/src/skf-campaign/references/step-07-capstone.md +40 -25
  209. package/src/skf-campaign/references/step-08-verify.md +28 -29
  210. package/src/skf-campaign/references/step-09-refine.md +29 -30
  211. package/src/skf-campaign/references/step-10-export.md +48 -36
  212. package/src/skf-campaign/references/step-11-maintenance.md +31 -34
  213. package/src/skf-campaign/references/step-resume.md +37 -68
  214. package/src/skf-campaign/scripts/campaign-deps.py +57 -18
  215. package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
  216. package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
  217. package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
  218. package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
  219. package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
  220. package/src/skf-campaign/scripts/campaign-report.py +204 -37
  221. package/src/skf-campaign/scripts/campaign-state.py +1128 -0
  222. package/src/skf-campaign/scripts/campaign-status.py +10 -74
  223. package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
  224. package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
  225. package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
  226. package/src/skf-campaign/templates/campaign-report-template.md +5 -1
  227. package/src/skf-campaign/templates/kickoff-template.md +0 -14
  228. package/src/skf-create-skill/SKILL.md +27 -20
  229. package/src/skf-create-skill/assets/compile-assembly-rules.md +6 -5
  230. package/src/skf-create-skill/assets/skill-sections.md +15 -5
  231. package/src/skf-create-skill/customize.toml +20 -8
  232. package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
  233. package/src/skf-create-skill/references/batch-mode.md +81 -0
  234. package/src/skf-create-skill/references/ccc-index-check.md +27 -0
  235. package/src/skf-create-skill/references/compile.md +107 -29
  236. package/src/skf-create-skill/references/component-extraction.md +113 -91
  237. package/src/skf-create-skill/references/enrich.md +41 -23
  238. package/src/skf-create-skill/references/entry-points-by-hand.md +20 -0
  239. package/src/skf-create-skill/references/extract.md +200 -161
  240. package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
  241. package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
  242. package/src/skf-create-skill/references/extraction-patterns.md +584 -196
  243. package/src/skf-create-skill/references/generate-artifacts.md +145 -93
  244. package/src/skf-create-skill/references/health-check.md +1 -1
  245. package/src/skf-create-skill/references/load-brief.md +53 -36
  246. package/src/skf-create-skill/references/relabel-rule.md +12 -0
  247. package/src/skf-create-skill/references/report.md +28 -59
  248. package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
  249. package/src/skf-create-skill/references/step-auto-shard.md +20 -93
  250. package/src/skf-create-skill/references/step-doc-rot.md +44 -60
  251. package/src/skf-create-skill/references/step-doc-sources.md +28 -19
  252. package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
  253. package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
  254. package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
  255. package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
  256. package/src/skf-create-skill/references/validate.md +173 -99
  257. package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
  258. package/src/skf-create-stack-skill/SKILL.md +29 -56
  259. package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
  260. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
  261. package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
  262. package/src/skf-create-stack-skill/customize.toml +23 -12
  263. package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
  264. package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
  265. package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
  266. package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
  267. package/src/skf-create-stack-skill/references/generate-output.md +138 -128
  268. package/src/skf-create-stack-skill/references/init.md +90 -32
  269. package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
  270. package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
  271. package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
  272. package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
  273. package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
  274. package/src/skf-create-stack-skill/references/report.md +39 -33
  275. package/src/skf-create-stack-skill/references/validate.md +20 -39
  276. package/src/skf-drop-skill/SKILL.md +28 -51
  277. package/src/skf-drop-skill/customize.toml +44 -30
  278. package/src/skf-drop-skill/references/execute.md +79 -146
  279. package/src/skf-drop-skill/references/invocation-contract.md +64 -0
  280. package/src/skf-drop-skill/references/report.md +46 -30
  281. package/src/skf-drop-skill/references/select.md +118 -124
  282. package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
  283. package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
  284. package/src/skf-export-skill/SKILL.md +40 -62
  285. package/src/skf-export-skill/assets/managed-section-format.md +14 -50
  286. package/src/skf-export-skill/assets/snippet-format.md +3 -1
  287. package/src/skf-export-skill/customize.toml +42 -13
  288. package/src/skf-export-skill/references/generate-snippet.md +54 -46
  289. package/src/skf-export-skill/references/health-check.md +2 -0
  290. package/src/skf-export-skill/references/invocation-contract.md +15 -0
  291. package/src/skf-export-skill/references/load-skill.md +88 -62
  292. package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
  293. package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
  294. package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
  295. package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
  296. package/src/skf-export-skill/references/package.md +15 -73
  297. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
  298. package/src/skf-export-skill/references/result-envelope.md +31 -16
  299. package/src/skf-export-skill/references/summary.md +41 -17
  300. package/src/skf-export-skill/references/token-report.md +4 -12
  301. package/src/skf-export-skill/references/update-context.md +118 -237
  302. package/src/skf-forger/SKILL.md +61 -37
  303. package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
  304. package/src/skf-forger/customize.toml +43 -0
  305. package/src/skf-forger/references/pipeline-mode.md +73 -18
  306. package/src/skf-forger/scripts/forge-status.py +299 -0
  307. package/src/skf-forger/scripts/parse-pipeline.py +322 -54
  308. package/src/skf-forger/scripts/pipeline-gate.py +428 -0
  309. package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
  310. package/src/skf-quick-skill/SKILL.md +54 -53
  311. package/src/skf-quick-skill/assets/skill-template.md +4 -2
  312. package/src/skf-quick-skill/customize.toml +51 -20
  313. package/src/skf-quick-skill/references/batch-mode.md +74 -21
  314. package/src/skf-quick-skill/references/compile.md +30 -31
  315. package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
  316. package/src/skf-quick-skill/references/finalize.md +69 -18
  317. package/src/skf-quick-skill/references/halt-contract.md +41 -29
  318. package/src/skf-quick-skill/references/health-check.md +5 -3
  319. package/src/skf-quick-skill/references/quick-extract.md +114 -45
  320. package/src/skf-quick-skill/references/registry-resolution.md +9 -64
  321. package/src/skf-quick-skill/references/resolve-target.md +116 -66
  322. package/src/skf-quick-skill/references/write-and-validate.md +79 -40
  323. package/src/skf-refine-architecture/SKILL.md +43 -53
  324. package/src/skf-refine-architecture/customize.toml +49 -20
  325. package/src/skf-refine-architecture/references/compile.md +97 -54
  326. package/src/skf-refine-architecture/references/exit-codes.md +30 -0
  327. package/src/skf-refine-architecture/references/finding-storage.md +21 -0
  328. package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
  329. package/src/skf-refine-architecture/references/improvements.md +14 -20
  330. package/src/skf-refine-architecture/references/init.md +98 -57
  331. package/src/skf-refine-architecture/references/issue-detection.md +32 -39
  332. package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
  333. package/src/skf-refine-architecture/references/report.md +65 -54
  334. package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
  335. package/src/skf-rename-skill/SKILL.md +19 -54
  336. package/src/skf-rename-skill/customize.toml +32 -26
  337. package/src/skf-rename-skill/references/execute.md +230 -184
  338. package/src/skf-rename-skill/references/exit-codes.md +7 -7
  339. package/src/skf-rename-skill/references/health-check.md +1 -7
  340. package/src/skf-rename-skill/references/invocation-contract.md +25 -0
  341. package/src/skf-rename-skill/references/report.md +63 -52
  342. package/src/skf-rename-skill/references/select.md +197 -115
  343. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
  344. package/src/skf-setup/SKILL.md +26 -26
  345. package/src/skf-setup/customize.toml +14 -10
  346. package/src/skf-setup/references/auto-index.md +50 -63
  347. package/src/skf-setup/references/ccc-index.md +29 -90
  348. package/src/skf-setup/references/detect-and-tier.md +58 -67
  349. package/src/skf-setup/references/health-check.md +2 -0
  350. package/src/skf-setup/references/invocation-contract.md +14 -0
  351. package/src/skf-setup/references/report.md +43 -166
  352. package/src/skf-setup/references/tier-rules.md +5 -5
  353. package/src/skf-setup/references/write-config.md +34 -57
  354. package/src/skf-test-skill/SKILL.md +17 -57
  355. package/src/skf-test-skill/assets/output-section-formats.md +33 -34
  356. package/src/skf-test-skill/customize.toml +27 -13
  357. package/src/skf-test-skill/references/coherence-check.md +134 -62
  358. package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
  359. package/src/skf-test-skill/references/coverage-check.md +217 -254
  360. package/src/skf-test-skill/references/detect-mode.md +5 -5
  361. package/src/skf-test-skill/references/external-validators.md +94 -82
  362. package/src/skf-test-skill/references/health-check.md +6 -7
  363. package/src/skf-test-skill/references/init.md +163 -88
  364. package/src/skf-test-skill/references/invocation-contract.md +66 -0
  365. package/src/skf-test-skill/references/migration-section-rules.md +5 -2
  366. package/src/skf-test-skill/references/report.md +162 -142
  367. package/src/skf-test-skill/references/score.md +80 -107
  368. package/src/skf-test-skill/references/scoring-rules.md +41 -155
  369. package/src/skf-test-skill/references/source-access-protocol.md +19 -92
  370. package/src/skf-test-skill/references/step-hard-gate.md +51 -33
  371. package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
  372. package/src/skf-test-skill/scripts/build-result-context.py +287 -0
  373. package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
  374. package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
  375. package/src/skf-test-skill/scripts/compute-score.py +223 -45
  376. package/src/skf-test-skill/scripts/gap-ledger.py +1381 -0
  377. package/src/skf-test-skill/scripts/hard-gate.py +203 -0
  378. package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
  379. package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
  380. package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
  381. package/src/skf-test-skill/scripts/score-signatures.py +459 -0
  382. package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
  383. package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
  384. package/src/skf-test-skill/scripts/verify-declared-numerator.py +127 -52
  385. package/src/skf-test-skill/templates/test-report-template.md +21 -15
  386. package/src/skf-update-skill/SKILL.md +38 -25
  387. package/src/skf-update-skill/customize.toml +27 -12
  388. package/src/skf-update-skill/references/detect-changes.md +233 -222
  389. package/src/skf-update-skill/references/gap-driven.md +336 -0
  390. package/src/skf-update-skill/references/health-check.md +31 -5
  391. package/src/skf-update-skill/references/init.md +250 -84
  392. package/src/skf-update-skill/references/invocation-contract.md +12 -0
  393. package/src/skf-update-skill/references/manual-section-rules.md +6 -23
  394. package/src/skf-update-skill/references/merge.md +180 -47
  395. package/src/skf-update-skill/references/re-extract.md +53 -262
  396. package/src/skf-update-skill/references/report.md +100 -35
  397. package/src/skf-update-skill/references/write.md +331 -149
  398. package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
  399. package/src/skf-verify-stack/SKILL.md +23 -42
  400. package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
  401. package/src/skf-verify-stack/customize.toml +55 -22
  402. package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
  403. package/src/skf-verify-stack/references/coverage.md +67 -64
  404. package/src/skf-verify-stack/references/exit-codes.md +21 -8
  405. package/src/skf-verify-stack/references/init.md +107 -62
  406. package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
  407. package/src/skf-verify-stack/references/integrations.md +106 -65
  408. package/src/skf-verify-stack/references/report.md +59 -42
  409. package/src/skf-verify-stack/references/requirements.md +32 -9
  410. package/src/skf-verify-stack/references/synthesize.md +53 -39
  411. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
  412. package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
  413. package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
  414. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
  415. package/tools/cli/commands/install.js +11 -4
  416. package/tools/cli/commands/status.js +15 -3
  417. package/tools/cli/commands/update.js +11 -4
  418. package/tools/cli/lib/tool-check.js +320 -0
  419. package/tools/cli/lib/version-check.js +22 -9
  420. package/tools/cli/skf-cli.js +4 -1
  421. package/.gitattributes +0 -16
  422. package/.nvmrc +0 -1
  423. package/docs/_internal/RELEASING.md +0 -679
  424. package/release-audits/v1.0.0-launch-audit.md +0 -728
  425. package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
  426. package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
  427. package/src/skf-campaign/manifest.yaml +0 -15
  428. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
  429. package/src/skf-create-skill/references/ecosystem-check.md +0 -74
  430. package/src/skf-drop-skill/references/headless-contract.md +0 -18
  431. package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
  432. package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
  433. package/src/skf-update-skill/references/validate.md +0 -124
  434. package/tools/validate-docs-drift.js +0 -233
  435. package/tools/validate-docs-links.js +0 -665
@@ -1,31 +1,24 @@
1
1
  ---
2
2
  nextStepFile: 'execute.md'
3
- versionPathsKnowledge: 'knowledge/version-paths.md'
4
- # Resolve `{manifestOpsHelper}` by probing `{manifestOpsProbeOrder}` in order
5
- # (installed SKF module path first, src/ dev-checkout fallback); first existing
6
- # path wins. §7 uses its `affected-versions` action to enumerate the versions a
7
- # rename must touch — the union of manifest version keys and on-disk version
8
- # dirs, deduped and semver-sorted (numeric, so 0.10.0 precedes 0.9.0). Unlike
9
- # execute.md's write helpers this one is not atomicity-critical: if neither path
10
- # resolves, §7 falls back to computing the union in the prompt.
3
+ # §1 resolves each probe order below (installed SKF module path first,
4
+ # src/ dev-checkout fallback; the first existing path wins).
5
+ # {manifestOpsHelper}: the §2 `read` and the §7 `affected-versions`.
11
6
  manifestOpsProbeOrder:
12
7
  - '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
13
8
  - '{project-root}/src/shared/scripts/skf-manifest-ops.py'
14
- # Resolve `{skillInventoryHelper}` by probing `{skillInventoryProbeOrder}`
15
- # (installed SKF module path first, src/ dev-checkout fallback). §3 uses it to
16
- # enumerate the rename candidates: the union of manifest `exports` and on-disk
17
- # skill directories, each with its version list and active version — computing
18
- # that union/count in the prompt has one correct answer per input. §3 falls
19
- # back to an in-prompt scan if neither path resolves.
9
+ # {skillInventoryHelper}: the §3 listing, §4a `--rename-check`, §6 `resolve`.
20
10
  skillInventoryProbeOrder:
21
11
  - '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
22
12
  - '{project-root}/src/shared/scripts/skf-skill-inventory.py'
23
- # `{renameNameValidator}` is this skill's own deterministic new-name gate. §5
24
- # uses it for the format / length / identity / collision checks (one correct
25
- # answer per (name, filesystem, manifest) state) and the interrupted-rename
26
- # recovery fingerprint; §5 falls back to the same checks in-prompt if Python is
27
- # unavailable.
13
+ # {renameNameValidator}: this skill's own new-name gate, run in §5.
28
14
  renameNameValidator: 'scripts/skf-validate-rename-name.py'
15
+ # {runLockHelper} and {emitEnvelopeHelper}: §4b and the halt procedure.
16
+ runLockProbeOrder:
17
+ - '{project-root}/_bmad/skf/shared/scripts/skf-run-lock.py'
18
+ - '{project-root}/src/shared/scripts/skf-run-lock.py'
19
+ emitEnvelopeProbeOrder:
20
+ - '{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py'
21
+ - '{project-root}/src/shared/scripts/skf-emit-result-envelope.py'
29
22
  ---
30
23
 
31
24
  <!-- Config: communicate in {communication_language}. -->
@@ -38,58 +31,85 @@ Identify the skill the user wants to rename, validate the new name against the a
38
31
 
39
32
  ## Rules
40
33
 
41
- - Focus only on selection, validation, and confirmation — do not modify manifest, copy, or delete files
34
+ - Focus only on selection, validation, and confirmation: this step writes only the run's own files (its run folder and run lock), never the manifest or a skill's files
42
35
  - Do not proceed without explicit user confirmation at the final gate
43
36
  - Do not accept a new name that fails validation (kebab-case, length, uniqueness)
44
37
  - Present the list of affected versions clearly so the user understands the scope
45
38
 
46
- **Headless error envelope (self-contained).** Where a halt below says *"emit the error envelope per SKILL.md 'Result Contract (Headless)'"*, write this single line to **stderr** (restated here so selection halts stay parseable even if SKILL.md is out of context) —
39
+ **Halt procedure.** Every HALT below names, in parentheses, its exit code, its `halt_reason` and the phase its `emit-halt` reports (see `references/exit-codes.md`), and runs these, in order, before it stops:
47
40
 
48
- ```
49
- SKF_RENAME_SKILL_RESULT_JSON: {"status":"error","old_name":"…|null","new_name":"…|null","versions_renamed":[],"manifest_rekeyed":false,"context_files_updated":[],"exit_code":<code>,"halt_reason":"<reason>","headless_decisions":[]}
50
- ```
41
+ 1. Once §4b has bound `{lock_owner}`, release the run lock (a release never removes a lock another run holds). From `{project-root}`:
42
+
43
+ ```bash
44
+ uv run {runLockHelper} release --lock "{forge_data_folder}/.skf-rename-{old_name}.lock" --owner "{lock_owner}"
45
+ ```
46
+
47
+ When it exits non-zero or prints no JSON, tell the user in one line that the lock file stays, and add `"warnings": ["run-lock-not-released: {forge_data_folder}/.skf-rename-{old_name}.lock"]` to the payload below.
48
+
49
+ 2. In `{headless_mode}`, pass the halt to the shared emitter, which prints its envelope on **stderr**. Write each string as JSON (escape `"`, `\` and control characters, and write every path with `/`), and pass `--run-dir "{run_dir}"` once §1 has bound it, even when the folder could not be created:
50
+
51
+ ```bash
52
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-rename-skill --run-dir "{run_dir}" --target stderr <<'SKF_HALT'
53
+ {"phase": "<phase>", "halt_reason": "<halt_reason>", "reason": "<the message the halt displays>", "path": "<the path the halt names; leave the key out when it names none>", "old_name": <"{old_name}", or null before §4 binds it>, "new_name": <"{new_name}", or null before §5 binds it>}
54
+ SKF_HALT
55
+ ```
51
56
 
52
- Use the `old_name`/`new_name`, `exit_code`, and `halt_reason` named at each halt site (see `references/exit-codes.md`) — `old_name`/`new_name` are `null` until resolved in §4/§5. `headless_decisions` is `[]` at every selection-stage halt: the only decision this step records (the §6 source-authority override) means *proceed*, so no recorded decision is ever paired with a halt here.
57
+ Display the `SKF_RENAME_SKILL_RESULT_JSON:` line it prints verbatim. When it exits non-zero and its `message` names the payload, fix the payload once and run it again. Only when `{emitEnvelopeHelper}` is missing, or the emitter still fails or prints no line, display the halt's message alone.
58
+
59
+ The halt leaves `{run_dir}` in place.
53
60
 
54
61
  ## MANDATORY SEQUENCE
55
62
 
56
- ### 1. Load Knowledge
63
+ ### 1. Start the Run
57
64
 
58
- Read `{versionPathsKnowledge}` completely and extract:
65
+ Resolve, as one batch of file-existence checks, `{runLockHelper}` ← first existing path in `{runLockProbeOrder}`, `{emitEnvelopeHelper}` ← first existing path in `{emitEnvelopeProbeOrder}`, `{manifestOpsHelper}` ← first existing path in `{manifestOpsProbeOrder}` and `{skillInventoryHelper}` ← first existing path in `{skillInventoryProbeOrder}`; all four stay bound for the rest of the run. From `{project-root}`, take the run id:
59
66
 
60
- - Path templates: `{skill_package}`, `{skill_group}`, `{forge_version}`, `{forge_group}`
61
- - Export manifest v2 schema (`schema_version`, `exports`, `active_version`, `versions` map, `status` field values)
62
- - The Rename section under "Skill Management Operations" — the complete list of 9 locations that must be updated coherently
67
+ ```bash
68
+ uv run {runLockHelper} run-id
69
+ ```
63
70
 
64
- You will use these templates and rules to build directory paths, enumerate affected versions, and plan the transactional rename in step 2.
71
+ Bind `{run_id}` ← its `run_id` and `{run_dir}` ← `{project-root}/_bmad-output/.skf-run/skf-rename-skill-{run_id}`. Then create the run folder and probe the folders the run writes, so a read-only mount or a full disk stops the run here rather than at step 2's copy:
65
72
 
66
- ### 2. Read Export Manifest
73
+ ```bash
74
+ for dir in "{run_dir}" "{skills_output_folder}" "{forge_data_folder}"; do
75
+ { mkdir -p "$dir" && printf 'probe' > "$dir/.skf-write-probe" && rm "$dir/.skf-write-probe"; } \
76
+ || { echo "not writable: $dir" >&2; exit 1; }
77
+ done
78
+ ```
67
79
 
68
- Load `{skills_output_folder}/.export-manifest.json` if it exists.
80
+ - When a helper has no existing candidate, or `run-id` exits non-zero or prints no JSON: HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:start-run`) with "**Rename Skill cannot start:** {the missing helper (`skf-run-lock.py`, `skf-emit-result-envelope.py`, `skf-manifest-ops.py` or `skf-skill-inventory.py`), or the helper's error}. Nothing was changed. Re-install SKF, then re-run." With no run id yet, its `emit-halt` leaves out `--run-dir "{run_dir}"`.
81
+ - When the loop exits non-zero: HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:write-probe`, with the folder its stderr names as `path`) with "**Cannot write to `{that folder}`.** Nothing was changed. Check that the path exists as a folder you can write to and that the disk has free space, then re-run."
69
82
 
70
- **If the file is missing or empty:** Treat as an empty manifest — proceed to section 3 and rely entirely on the on-disk directory scan. Drafted or never-exported skills can still be renamed. Store `manifest_exists = false` for later use in step 2 (section 6 will not attempt to update a manifest that does not exist).
83
+ Once the loop passes and `{customization_resolver_unavailable}` is set (SKILL.md On Activation step 3), record the reason in the run sink: write `customization_resolver_unavailable: {customization_resolver_unavailable}` to `{run_dir}/resolver-warning.txt` with a file write, never `echo` (the reason can hold quotes or `$( )`), then run `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "$(cat "{run_dir}/resolver-warning.txt")"`, and delete that file (`rm -f "{run_dir}/resolver-warning.txt"`), so the report step's `rmdir` still finds the folder empty.
71
84
 
72
- **If the file exists but contains no `exports` entries:** Same handling — proceed to section 3 with the directory scan. Store `manifest_exists = true` so step 2 still touches the (empty) manifest on write.
85
+ ### 2. Read Export Manifest
86
+
87
+ Read the manifest through `{manifestOpsHelper}`, never by eye: it migrates v1 to v2, normalizes `platforms` to `ides`, and reports a file that does not parse:
73
88
 
74
- **If the file exists with entries:** Parse JSON and verify `schema_version` is `"2"`. If the manifest is v1 (no `schema_version` field), note this but continue — treat every entry as having a single active version derived from its current state. Store `manifest_exists = true`.
89
+ ```bash
90
+ uv run {manifestOpsHelper} {skills_output_folder} read
91
+ ```
75
92
 
76
- **Hard halt condition:** If the file exists but is malformed (not valid JSON), halt with: "**Export manifest is corrupt** at `{skills_output_folder}/.export-manifest.json` — fix or remove the file before renaming." HALT (exit code 3, `halt_reason: "manifest-corrupt"`). In headless mode, emit the error envelope per SKILL.md "Result Contract (Headless)" with `old_name: null`, `new_name: null`.
93
+ - **`status == "error"`** (the file exists but does not parse): halt with "**Export manifest is corrupt** at `{skills_output_folder}/.export-manifest.json`: {error}. Fix or remove the file before renaming." HALT (exit code 3, `halt_reason: "manifest-corrupt"`, `emit-halt` phase `select:read-manifest`, with the manifest as `path`).
94
+ - **`status == "ok"`**: use `result.manifest` (already in the v2 shape) as `manifest` for the rest of this step. Set `manifest_exists = true` when `manifest.exports` has at least one entry, else `false` (a missing file reads back as an empty `exports`). With no entry, §3's on-disk scan is the whole roster: drafted or never-exported skills can still be renamed, and step 2 §6 has no manifest entry to re-key.
95
+ - **No JSON on stdout** (the helper could not run): HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:read-manifest`) with "**Rename Skill cannot read the export manifest:** {the helper's stderr}. Nothing was changed. Re-install SKF, then re-run."
77
96
 
78
97
  ### 3. List Available Skills
79
98
 
80
- Enumerate every skill available for rename deterministically. Resolve `{skillInventoryHelper}` ← first existing path in `{skillInventoryProbeOrder}` and run:
99
+ Enumerate every skill available for rename deterministically:
81
100
 
82
101
  ```bash
83
- python3 {skillInventoryHelper} {skills_output_folder}
102
+ uv run {skillInventoryHelper} {skills_output_folder} --forge-data-folder {forge_data_folder}
84
103
  ```
85
104
 
86
- Read the JSON. Each entry in `skills[]` carries `name`, its `versions` array, and `active_version` (the helper unions the manifest `exports` with the on-disk directories and dedupes, so manifest-tracked and orphaned skills both appear). A skill whose `name` is absent from `manifest.exports` is a draft/orphan the rename workflow can still handle — annotate it "(not in manifest)".
105
+ Read the JSON. Each entry in `skills[]` carries `name`, its `versions` array, and `active_version` (the helper unions the manifest `exports` with the on-disk directories and dedupes, so manifest-tracked and orphaned skills both appear), plus `skf_skill`. List only the entries whose `skf_skill` is true: rename copies and then deletes the whole folder, so a folder SKF did not generate is never offered. A listed skill whose `name` is absent from `manifest.exports` is a draft/orphan the rename workflow can still handle: annotate it "(not in manifest)". The §4a rename check decides whether the skill the user picks can move. Bind `{not_skf_output}` ← `not_skf_output` (folders holding a skill SKF did not generate); when it is non-empty, show one line under the list: "Not offered (not SKF output): {not_skf_output}". When the call prints no JSON, HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:list-skills`) with "**Rename Skill cannot list the skills:** {the helper's stderr}. Nothing was changed. Re-install SKF, then re-run."
87
106
 
88
- **If `{skillInventoryHelper}` has no existing candidate** (neither probe path resolves — e.g. Python/`uv` unavailable): build the list in the prompt instead — read `exports` from the manifest (if `manifest_exists`), scan `{skills_output_folder}/` for top-level directories, union the two, and read each one's `active_version` and version count. Directories absent from `exports` are the "(not in manifest)" entries.
107
+ **If the list to display is empty** (no `skills[]` entry whose `skf_skill` is true):
89
108
 
90
- **If the list is empty** (no manifest entries AND no on-disk skill directories): halt with "**Rename Skill — nothing to rename.** No skills found in `{skills_output_folder}/`. Run `[CS] Create Skill` first." HALT (exit code 3, `halt_reason: "nothing-to-rename"`). In headless mode, emit the error envelope with `old_name: null`, `new_name: null`.
109
+ - When `{old_name_arg}` is set and it is in `{not_skf_output}`, take the §4a refusal for a folder SKF did not generate first, in either mode (exit code 5, `halt_reason: "not-skf-output"`, `emit-halt` phase `select:ownership-check`, with `old_name: "{old_name}"` in its payload).
110
+ - Otherwise halt with "**Rename Skill: nothing to rename.** No skill SKF generated was found in `{skills_output_folder}/`. Run `[CS] Create Skill` first." When `{not_skf_output}` is non-empty, append: "Left untouched (not SKF output): {not_skf_output}." HALT (exit code 3, `halt_reason: "nothing-to-rename"`, `emit-halt` phase `select:list-skills`).
91
111
 
92
- Display the list, one line per skill as `{name} ({n} versions, active: {active_version})` (append "(not in manifest)" for orphans):
112
+ Display the list only when §4 asks for the skill, one line per skill as `{name} ({n} versions, active: {active_version})` (append "(not in manifest)" for orphans):
93
113
 
94
114
  ```
95
115
  **Rename Skill — select target**
@@ -98,88 +118,118 @@ Available skills:
98
118
  1. cognee (3 versions, active: 0.6.0)
99
119
  2. express (1 version, active: 4.18.0)
100
120
  3. legacy-helper (not in manifest)
121
+
122
+ Not offered (not SKF output): my-module-skill
101
123
  ```
102
124
 
103
125
  ### 4. Ask Which Skill
104
126
 
127
+ **GATE [default: use args]**: When `{old_name_arg}` is set, it is the input below in either mode, and the question is not shown. Otherwise, in `{headless_mode}`, HALT (exit code 2, `halt_reason: "input-missing"`, `emit-halt` phase `select:ask-old-name`): "headless mode requires old_name argument." Otherwise display the §3 list and ask:
128
+
105
129
  "**Which skill would you like to rename?**
106
130
  Enter the skill name or its number from the list above, or `cancel` / `exit` / `:q` to abort."
107
131
 
108
- Wait for user input. Accept either the numeric index or the skill name (exact match). **GATE [default: use args]** — If `{headless_mode}` and old skill name was provided as argument: select that skill and auto-proceed. If not provided, HALT (exit code 2, `halt_reason: "input-missing"`): "headless mode requires old_name argument." In headless, emit the error envelope.
132
+ Wait for user input. Accept either the numeric index or the skill name (exact match); `{old_name_arg}` must be a name.
109
133
 
110
- - If the user enters `cancel`, `exit`, `[X]`, `q`, or `:q`: Display "Cancelled — no changes were made." and HALT (exit code 6, `halt_reason: "user-cancelled"`).
111
- - **If the user's input does not match any listed skill:** Re-display the list and ask again.
134
+ - If the user enters `cancel`, `exit`, `[X]`, `q`, or `:q`: Display "Cancelled: no changes were made." and HALT (exit code 6, `halt_reason: "user-cancelled"`, `emit-halt` phase `select:ask-old-name`).
135
+ - **If the input names a folder in `{not_skf_output}`:** take the §4a refusal for a folder SKF did not generate.
136
+ - **If the user's input does not match any listed skill:**
137
+ - **Interactive:** say that it matches no listed skill, display the §3 list and ask again.
138
+ - **Headless (`{headless_mode}` is true):** the supplied `old_name` argument matches no listed skill and there is no further input to re-prompt for. HALT (exit code 2, `halt_reason: "input-invalid"`, `emit-halt` phase `select:ask-old-name`): "headless mode: old_name argument `{supplied value}` does not match any listed skill."
112
139
 
113
140
  Store the selection as `old_name`.
114
141
 
142
+ ### 4a. Ownership Check
143
+
144
+ Rename copies the whole `{skills_output_folder}/{old_name}/` folder and its forge folder, then deletes the old ones, so it only renames a skill SKF generated in the versioned layout. The inventory helper decides that; never decide by hand whether SKF generated a folder. Run:
145
+
146
+ ```bash
147
+ uv run {skillInventoryHelper} "{skills_output_folder}" --skill {old_name} --rename-check --forge-data-folder "{forge_data_folder}"
148
+ ```
149
+
150
+ From its `rename_check`, bind `{rename_verdict}` ← `verdict`, `{rename_reason}` ← `reason`, `{rename_detail}` ← `detail`, `{rename_entries}` ← `offending_entries`, `{target_errors}` ← `errors`, `{forge_move}` ← `forge_move`, `{forge_left_in_place}` ← `forge_left_in_place` and `{same_folder}` ← `same_folder`. The helper applies the rules below in order, to the skill folder and, unless both settings name one folder, to its forge folder, and returns the first that refuses. When the call exits non-zero, or its result has no `rename_check` (an installed `skf-skill-inventory.py` older than the flag), refuse with rule 1.
151
+
152
+ When `{rename_verdict}` is not `"ok"`, display the message of the rule that applies and HALT (exit code 5, `halt_reason: "{rename_verdict}"`, `emit-halt` phase `select:ownership-check`, with `old_name: "{old_name}"` in its payload): `not-skf-output`, or `flat-layout` for rule 4. No lock is held yet (§4b takes it), so the halt releases none.
153
+
154
+ 1. No verdict (a failed call, or no `rename_check`) → `not-skf-output`: "**SKF cannot check that it generated `{old_name}`:** the rename check of `skf-skill-inventory.py` failed, or the installed one has none. Nothing was changed. Re-install SKF and re-run the rename."
155
+ 2. `{rename_reason}` is `foreign`, `reserved-name` or `absent` (no skill SKF generated at `{skills_output_folder}/{old_name}`) → `not-skf-output`: "**`{old_name}` is not SKF output: nothing was changed.** `{skills_output_folder}/{old_name}/` has no SKF marker in its `metadata.json`, so SKF will not rename it. A shared `{skills_output_folder}` is supported: SKF leaves the skills it did not generate alone, so manage `{old_name}` yourself. Only if `{skills_output_folder}` holds a module's own source rather than skills, set `skills_output_folder` in `{project-root}/_bmad/skf/config.yaml` to a folder of its own, move your SKF skills there and re-run `/skf-setup`." When `{target_errors}` is non-empty (for example, the folder is a link), or the reason is `reserved-name` or `absent`, show `{rename_detail}` in place of the marker sentence.
156
+ 3. `mixed` → `not-skf-output`: "**`{skills_output_folder}/{old_name}/` also holds entries SKF did not generate:** {rename_entries}. Nothing was changed. Rename moves and then deletes the whole folder, so SKF will not rename it. Move those entries out of the folder and re-run. A version folder with no `metadata.json` can also be one that an interrupted update-skill run left behind; delete it yourself in that case."
157
+ 4. `flat-layout` → `flat-layout`: "**`{old_name}` still uses the flat layout.** Nothing was changed. Rename works on the versioned layout only: run `@Ferris TS {old_name}` (or US, AS or EX) once to move it into that layout, then re-run the rename."
158
+ 5. `forge-link`, `forge-not-a-folder` or `forge-unreadable` (the forge folder is a link, is not a folder, or cannot be listed) → `not-skf-output`: "**SKF will not move `{forge_data_folder}/{old_name}`:** {rename_detail}. Nothing was changed." For `forge-link`, add: "SKF never moves or deletes through a link: the copy would take the files from where the link points, and removing the old name or rolling back would empty that folder. Replace the link with the folder it points to, then re-run." Otherwise (the path is not a folder, or SKF cannot list it), add: "Rename moves a forge folder only when it is a folder SKF can read. Move that path out of the way or fix its permissions, then re-run."
159
+ 6. `forge-mixed` → `not-skf-output`: "**`{forge_data_folder}/{old_name}/` also holds entries SKF did not generate:** {rename_entries}. Nothing was changed. Rename moves and then deletes the whole forge folder, so SKF will not rename it. Move those entries out of the folder and re-run."
160
+
161
+ When no rule applies, `{forge_move}` and `{forge_left_in_place}` come from the helper. Then continue to §4b.
162
+
115
163
  ### 4b. Concurrency Guard
116
164
 
117
- Two concurrent rename runs against the same `old_name` would corrupt state mid-copy: one would `rm -rf` the other's freshly-staged new directories, or both would race on the manifest re-key. The lock below catches the common accidental-double-invoke case. It is a **best-effort PID-file guard**, not a held flock — the LLM-driven workflow spans many turn boundaries and no single bash invocation can hold flock across them.
165
+ **Skip this section when `--dry-run` is set:** a dry run writes nothing to the skill, so it takes no lock and never blocks a rename.
118
166
 
119
- **Mirror this exactly so the guard works the same way every run:**
167
+ Two concurrent rename runs against the same `old_name` would corrupt state mid-copy: one would `rm -rf` the other's freshly-staged new directories, or both would race on the manifest re-key. Take the run lock, a file that names the run holding it. From `{project-root}`, run:
120
168
 
121
169
  ```bash
122
- LOCK={forge_data_folder}/{old_name}/.skf-rename.lock
123
- mkdir -p "$(dirname "$LOCK")"
124
-
125
- if [ -f "$LOCK" ]; then
126
- HELD_PID=$(head -n1 "$LOCK" 2>/dev/null | awk '{print $1}')
127
- if [ -n "$HELD_PID" ] && kill -0 "$HELD_PID" 2>/dev/null; then
128
- echo "skf-rename-skill: another rename is in progress (pid=$HELD_PID, started $(awk 'NR==2' "$LOCK" 2>/dev/null))"
129
- exit 1
130
- fi
131
- echo "skf-rename-skill: clearing stale lock from pid=$HELD_PID"
132
- fi
133
-
134
- printf '%s\n%s\n' "$$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$LOCK"
170
+ uv run {runLockHelper} acquire \
171
+ --lock "{forge_data_folder}/.skf-rename-{old_name}.lock" \
172
+ --owner "rename-skill:{old_name}:{run_id}" \
173
+ --stale-after 60
135
174
  ```
136
175
 
137
- **Halt protocol on live-PID collision:**
176
+ The lock sits beside the forge folders, never inside one: a skill can have no forge folder, or one SKF did not generate, and a lock inside it would be copied into the new one. The helper creates `{forge_data_folder}` when it is missing.
177
+
178
+ It prints one JSON line. Dispatch on its exit code:
179
+
180
+ - **0** (`acquired` true): bind `{lock_owner}` ← `owner`; every renewal and release passes that value. When `stale_replaced` is not null, a run that stopped without releasing its lock left it: display the helper's `message` and record the warning in the run sink, with `an unnamed owner` for a null `held_by` (a lock file an older SKF version wrote):
181
+
182
+ ```bash
183
+ uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "run-lock-replaced: {stale_replaced.held_by} since {stale_replaced.held_since}"
184
+ ```
138
185
 
139
- - Display: `"**Another rename is in progress.** The skill {old_name} is locked by pid={HELD_PID} (started {timestamp from line 2 of the lock file}). Wait for that run to finish, or — if you know that pid is no longer running — delete {LOCK} manually and re-run."`
140
- - HALT (exit code 5, `halt_reason: "halted-for-concurrent-run"`). In `{headless_mode}`, emit the error envelope per SKILL.md "Result Contract (Headless)" with `old_name: "{old_name}"`, `new_name: null`. **No `headless_decisions[]` entry** — this is a hard halt before any gate fires.
186
+ When `record` exits non-zero, show its message and go on: the warning is advisory.
141
187
 
142
- **Release contract:**
188
+ - **3** (`acquired` false): another rename of `{old_name}` holds a lock that has not gone stale. Display "**Another rename of `{old_name}` is in progress.** {message}", with the helper's `message`, and HALT (exit code 5, `halt_reason: "halted-for-concurrent-run"`, `emit-halt` phase `select:concurrency-guard`, with the lock file as `path`). This run took no lock, so the halt releases none.
189
+ - **Any other exit, or no JSON:** HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:concurrency-guard`, with the lock file as `path`): "**The run lock could not be taken:** {the `message` the helper printed on stderr}. Nothing was changed." No lock was taken, so none is released.
143
190
 
144
- - This run owns the lock only after the `printf … > "$LOCK"` line above runs — i.e. once §4b has cleared the live-PID check and written this run's PID. The live-PID collision halt directly above is the **sole exception**: it exits *before* that line, so the lock belongs to the other live run and this run must **never** delete it (clearing a live lock it just honored would let a third invocation run concurrently).
145
- - The terminal health-check step (step 4) deletes the lock as its final action on the success path.
146
- - **Every halt from §5 onward — after this run has acquired the lock — must delete it before exiting** (`rm -f "$LOCK"` per halt site): the §5 input/format/collision halts, the §6 source-authority and cancel halts, the §8 cancel and dry-run exits, and execute.md's copy/verify/manifest/write rollbacks. Otherwise the next attempt would see a stale lock from this run.
191
+ A crashed run's lock goes stale 60 minutes after it was taken or last renewed, so step 2 §1 renews it before the copy.
147
192
 
148
193
  ### 5. Ask for New Name
149
194
 
195
+ **GATE [default: use args]**: When `{new_name_arg}` is set, it is the candidate in either mode, and the question is not shown. Otherwise, in `{headless_mode}`, HALT (exit code 2, `halt_reason: "input-missing"`, `emit-halt` phase `select:ask-new-name`): "headless mode requires new_name argument." Otherwise ask:
196
+
150
197
  "**What is the new name for this skill?**
151
198
  The new name must be kebab-case: lowercase alphanumeric with hyphens, 1-64 characters, matching the regex `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$` (starts and ends with a lowercase letter or digit; a single letter or digit is valid). Or type `cancel` / `exit` / `:q` to abort."
152
199
 
153
- Wait for user input. Trim whitespace. **GATE [default: use args]** — If `{headless_mode}` and new_name was provided as argument: use it and auto-proceed through validation. If not provided, release the lock and HALT (exit code 2, `halt_reason: "input-missing"`): "headless mode requires new_name argument." In headless, emit the error envelope.
200
+ Wait for user input. Trim whitespace.
154
201
 
155
- - If the user enters `cancel`, `exit`, `[X]`, `q`, or `:q`: release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "Cancelled — no changes were made.", and HALT (exit code 6, `halt_reason: "user-cancelled"`).
202
+ - If the user enters `cancel`, `exit`, `[X]`, `q`, or `:q`: display "Cancelled: no changes were made." and HALT (exit code 6, `halt_reason: "user-cancelled"`, `emit-halt` phase `select:ask-new-name`).
156
203
 
157
- **Validate the candidate deterministically.** Run `{renameNameValidator}` (a per-skill helper, always shipped with the skill) — it applies format, length, identity, and collision in that order and returns the verdict as JSON:
204
+ **Validate the candidate deterministically** with `{renameNameValidator}`, which applies format, length, identity and collision in that order and returns the verdict as JSON:
158
205
 
159
206
  ```bash
160
- python3 {renameNameValidator} \
207
+ uv run {renameNameValidator} \
161
208
  --old-name {old_name} --new-name {candidate} \
162
209
  --skills-output-folder {skills_output_folder} \
163
210
  --forge-data-folder {forge_data_folder}
164
211
  ```
165
212
 
166
- The checks: **format** = the kebab regex `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$` (the module's canonical rule, same as `skf-validate-output.py` / `skf-validate-brief-inputs.py`, so a digit-leading name like `3d-tools` renames cleanly); **length** = 1-64 characters (agentskills.io spec); **identity** = differs from `{old_name}`; **collision** = the name is not a manifest `exports` key, a `{skills_output_folder}` directory, or a `{forge_data_folder}` directory.
213
+ When it prints no JSON, HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:validate-new-name`) with "**Rename Skill cannot check the new name:** {the validator's stderr}. Nothing was changed. Re-install SKF, then re-run."
167
214
 
168
- Read `valid`, `first_failure`, `checks`, and `interrupted_rename`. If `valid` is true, store the input as `new_name` and proceed to §6. Otherwise branch on `first_failure` — interactive: display the message and re-ask; headless: release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), HALT with the mapped code, and emit the error envelope:
215
+ Read `valid`, `first_failure`, `checks`, `interrupted_rename`, `interrupted_after_rekey` and `leftover_folders`. If `valid` is true, store the input as `new_name` and proceed to §6. When `interrupted_after_rekey` is true, take the recovery halt below the list, in both modes. Otherwise branch on `first_failure`. Interactive: display the message and ask the question above again, also when the candidate came from `{new_name_arg}`. Headless: HALT with the mapped code:
169
216
 
170
- - **`format`** → "**Invalid name format.** The new name must be lowercase alphanumeric with hyphens, starting and ending with a lowercase letter or digit. Try again." (headless HALT: exit code 2, `halt_reason: "input-invalid"`)
171
- - **`length`** → "**Invalid name length.** The new name must be 1-64 characters. Try again." (headless HALT: exit code 2, `halt_reason: "input-invalid"`)
172
- - **`identity`** → "**The new name is identical to the current name.** Nothing to rename. Try again or abort the workflow." (headless HALT: exit code 2, `halt_reason: "input-invalid"`)
173
- - **`collision`** → "**Name collision.** `{new-name}` already exists at: {the `path` of each entry in `checks.collision.locations`}. Pick a different name." When `interrupted_rename` is true, append: "This may be a stranded partial rename from an earlier interrupted run — `{new_name}` was staged but `{old_name}` was never removed. Confirm the `{new_name}` directories are not a skill you want to keep, then clean them up (`rm -rf {skills_output_folder}/{new_name} {forge_data_folder}/{new_name}`) and re-run this rename." — this gives headless pipelines a named recovery path instead of a dead-end collision halt. (headless HALT: exit code 5, `halt_reason: "name-collision"`)
217
+ - **`format`** → "**Invalid name format.** The new name must be lowercase alphanumeric with hyphens, starting and ending with a lowercase letter or digit. Try again." (headless HALT: exit code 2, `halt_reason: "input-invalid"`, `emit-halt` phase `select:validate-new-name`)
218
+ - **`length`** → "**Invalid name length.** The new name must be 1-64 characters. Try again." (headless HALT: exit code 2, `halt_reason: "input-invalid"`, `emit-halt` phase `select:validate-new-name`)
219
+ - **`identity`** → "**The new name is identical to the current name.** Nothing to rename. Try again or abort the workflow." (headless HALT: exit code 2, `halt_reason: "input-invalid"`, `emit-halt` phase `select:validate-new-name`)
220
+ - **`collision`** → "**Name collision.** `{new-name}` already exists at: {the `path` of each entry in `checks.collision.locations`}. Pick a different name." When `checks.collision.locations` has a `reserved` entry, say instead: "**Reserved name.** `improvement-queue` is SKF's own folder in `{forge_data_folder}`. Pick a different name." When `interrupted_rename` is true, append: "This may be a stranded partial rename from an earlier interrupted run: `{new_name}` was staged but `{old_name}` was never removed. Confirm the `{new_name}` directories are not a skill you want to keep, then remove `{skills_output_folder}/{new_name}`, and `{forge_data_folder}/{new_name}` only when it holds a copy of `{old_name}`'s forge folder, and re-run this rename." This gives headless pipelines a named recovery path instead of a dead-end collision halt. (headless HALT: exit code 5, `halt_reason: "name-collision"`, `emit-halt` phase `select:validate-new-name`)
174
221
 
175
- **If `{renameNameValidator}` cannot run** (Python/`uv` unavailable): apply the same four checks in the same order in the prompt — the kebab regex, the 1-64 length bound, inequality with `{old_name}`, and the three-source collision lookup (plus the interrupted-rename fingerprint above) — using the identical messages and halt mapping.
222
+ **Recovery after the manifest re-key.** When `interrupted_after_rekey` is true, an earlier rename of `{old_name}` to `{new_name}` stopped between its manifest re-key and its delete, and `leftover_folders` lists the old folders it left. Re-asking would invite a third name and a second copy of the skill, so in both modes display "**An earlier rename of `{old_name}` to `{new_name}` stopped after it moved the export manifest to `{new_name}`.** `{new_name}` is the renamed skill, and these old folders are still on disk: {leftover_folders}. Nothing was changed. If `{old_name}` is not a skill you want to keep, finish that rename: delete them with SKF's checked delete, `uv run {skillInventoryHelper} guarded-delete --root "{skills_output_folder}" --root "{forge_data_folder}" {each path in leftover_folders, quoted}`, then run `[EX] Export Skill` to rebuild the context files that may still name `{old_name}`." and HALT (exit code 5, `halt_reason: "name-collision"`, `emit-halt` phase `select:validate-new-name`).
176
223
 
177
224
  ### 6. Source Authority Check
178
225
 
179
- Resolve `{skill_package}` for the active version using the manifest:
180
- `{skills_output_folder}/{old_name}/{active_version}/{old_name}/metadata.json`
226
+ Read `source_authority` from the `metadata.json` of the version `resolve` picks (the manifest's `active_version`, or the `active` link's when the manifest lags it):
227
+
228
+ ```bash
229
+ uv run {skillInventoryHelper} resolve "{skills_output_folder}" --skill {old_name} --forge-data-folder "{forge_data_folder}"
230
+ ```
181
231
 
182
- Read the `source_authority` field (if present).
232
+ From its `resolve` object bind `{source_metadata}` ← `paths.metadata.path` and read the `source_authority` field of that file (if present). When the call fails or `{source_metadata}` is null, there is no `metadata.json` to read: treat `source_authority` as absent.
183
233
 
184
234
  **If `source_authority == "official"`:**
185
235
 
@@ -193,34 +243,41 @@ registry will still get the original name. Rename is a LOCAL operation only —
193
243
  does not rename anything at the registry.
194
244
  ```
195
245
 
196
- Ask: "**Continue anyway?** [Y/N] (or `cancel` / `exit` / `:q` to abort)"
246
+ **GATE [default: HALT in headless]**: When `{acknowledge_official}` is true, `--acknowledge-official` answers this warning for this run in either mode: set `source_authority_override = true` and proceed without asking. Otherwise, in `{headless_mode}`, proceed only when `{forceSourceAuthorityInHeadless}` is true (set `source_authority_override = true`); when it is not, HALT (exit code 5, `halt_reason: "source-authority-blocked"`, `emit-halt` phase `select:source-authority`) with "**`{old_name}` is an official skill** (its `source_authority` is `official`). Nothing was changed. To rename it in a headless run, pass `--acknowledge-official` with the invocation, or set `force_source_authority_in_headless` to `true` in your team or personal customization of skf-rename-skill to approve every headless rename of an official skill." Otherwise ask: "**Continue anyway?** [Y/N] (or `cancel` / `exit` / `:q` to abort)"
197
247
 
198
248
  Wait for response.
199
- - **If `N`** (or `cancel` / `exit` / `[X]` / `:q`) → release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "**Cancelled.** No changes were made.", HALT (exit code 6, `halt_reason: "user-cancelled"`).
249
+ - **If `N`** (or `cancel` / `exit` / `[X]` / `:q`) → display "**Cancelled.** No changes were made." and HALT (exit code 6, `halt_reason: "user-cancelled"`, `emit-halt` phase `select:source-authority`).
200
250
  - **If `Y`** → proceed. Set `source_authority_override = true`.
201
251
 
202
- **Headless behavior:** If `{headless_mode}` is true AND `{forceSourceAuthorityInHeadless}` is `"true"`, auto-proceed and record `{gate: "source-authority", default_action: "halt", taken_action: "proceed", reason: "force_source_authority_in_headless override"}` in `headless_decisions[]`. Otherwise, release the lock and HALT (exit code 5, `halt_reason: "source-authority-blocked"`) and emit the error envelope — the safe default protects against silent registry-divergence on `published`-tagged skills.
252
+ **Record a headless pass.** When `{headless_mode}` is true and the run proceeded past the warning, record the decision in the run sink, with `{source_authority_reason}` ← `--acknowledge-official flag` when `{acknowledge_official}` is true, else `force_source_authority_in_headless override`. From `{project-root}`:
253
+
254
+ ```bash
255
+ cat > "{run_dir}/decision.json" <<'SKF_DECISION'
256
+ {"gate": "source-authority", "default_action": "halt", "taken_action": "proceed", "reason": "{source_authority_reason}"}
257
+ SKF_DECISION
258
+ uv run {emitEnvelopeHelper} record --workflow skf-rename-skill --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"
259
+ ```
260
+
261
+ When `record` exits non-zero, the decision cannot reach the audit trail: HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:source-authority`) with its message.
203
262
 
204
263
  **If `source_authority` is absent, or any value other than `"official"`:** skip the warning and proceed.
205
264
 
206
265
  ### 7. Enumerate Affected Versions
207
266
 
208
- Resolve `{manifestOpsHelper}` ← first existing path in `{manifestOpsProbeOrder}` (installed SKF module path first, `src/` dev-checkout fallback). Enumerate every version the rename must touch deterministically via its `affected-versions` action:
267
+ Enumerate every version the rename must touch, the manifest's and the ones on disk, through the `affected-versions` action of `{manifestOpsHelper}`:
209
268
 
210
269
  ```bash
211
- python3 {manifestOpsHelper} {skills_output_folder} affected-versions {old_name}
270
+ uv run {manifestOpsHelper} {skills_output_folder} affected-versions {old_name}
212
271
  ```
213
272
 
214
- Read the JSON result. Store `affected_versions` = `result.affected_versions` and `affected_versions_count` = `result.count`. The helper unions the manifest's `exports.{old_name}.versions` keys with the on-disk version directories under `{skills_output_folder}/{old_name}/` (every entry that is not the `active` symlink), so it handles both manifest-tracked and orphaned on-disk versions, deduplicates, and applies a **numeric** semver-descending sort (so `0.10.0` correctly precedes `0.9.0`, which a lexical sort gets wrong). An incomplete union would risk leaving a version internally un-renamed in the new copy — a leftover that step 2 §5 only catches for the versions it was told about.
215
-
216
- **If `{manifestOpsHelper}` has no existing candidate** (neither probe path resolves — e.g. Python/`uv` unavailable): compute `affected_versions` in the prompt instead — read every key under `exports.{old_name}.versions` in the manifest, list every directory under `{skills_output_folder}/{old_name}/` that is not `active`, union the two sets, and sort descending (newest first, comparing version components numerically). Store the list as `affected_versions` and its length as `affected_versions_count`.
273
+ Store `affected_versions` = `result.affected_versions` (newest first) and `affected_versions_count` = `result.count`. When it prints no JSON, or its `status` is not `"ok"`, HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:affected-versions`) with "**Rename Skill cannot list the versions of `{old_name}`:** {its `error`, or the helper's stderr}. Nothing was changed."
217
274
 
218
- Also resolve the four outer paths using the templates from `{versionPathsKnowledge}`:
275
+ Also bind the four outer paths, each without a trailing `/`:
219
276
 
220
- - `old_skill_group` = `{skills_output_folder}/{old_name}/`
221
- - `new_skill_group` = `{skills_output_folder}/{new_name}/`
222
- - `old_forge_group` = `{forge_data_folder}/{old_name}/`
223
- - `new_forge_group` = `{forge_data_folder}/{new_name}/`
277
+ - `old_skill_group` = `{skills_output_folder}/{old_name}`
278
+ - `new_skill_group` = `{skills_output_folder}/{new_name}`
279
+ - `old_forge_group` = `{forge_data_folder}/{old_name}`
280
+ - `new_forge_group` = `{forge_data_folder}/{new_name}`
224
281
 
225
282
  ### 8. Confirmation Gate
226
283
 
@@ -235,50 +292,75 @@ Display the full operation summary:
235
292
 
236
293
  Directories that will be copied then removed:
237
294
  {old_skill_group} → {new_skill_group}
238
- {old_forge_group} → {new_forge_group}
295
+ {if forge_move:}{old_forge_group} → {new_forge_group}
296
+ {if forge_left_in_place:}Left in place (not SKF output): {forge_left_in_place} — SKF did not generate it, so it keeps its name.
239
297
 
240
298
  Inside each version, the inner `{old_name}/` directory will be renamed to `{new_name}/`,
241
299
  and the following files will be updated:
242
300
  - SKILL.md (frontmatter `name` field)
243
- - metadata.json (`name` field)
244
- - context-snippet.md (display name and root paths)
245
- - provenance-map.json (`skill_name` field, under {old_forge_group})
301
+ - metadata.json (`name` field, and paths into the moved folders)
302
+ - context-snippet.md (the name in its header, its IMPORTANT line and its root paths)
303
+ {if forge_move or same_folder:}- provenance-map.json (`skill_name` field and paths into the moved folders, under {old_forge_group})
246
304
 
247
305
  Manifest `exports.{old_name}` will be re-keyed to `exports.{new_name}`.
248
306
  Platform context files (CLAUDE.md, .cursorrules, AGENTS.md) will be rebuilt so
249
307
  the managed section references `{new_name}` instead of `{old_name}`.
308
+ Copies installed in your IDE skill folders (`npx skills add`) are not renamed;
309
+ unless `snippet_skill_root_override` is set, the report names any it finds.
250
310
 
251
- Operation is **transactional** — the new name will be fully materialized and verified
252
- before the old name is removed. If any step fails before the final delete, the new
253
- directories are removed and the old skill remains intact.
311
+ Operation is **transactional**: the new name will be fully materialized and verified
312
+ before the old name is removed. If a step fails up to the manifest re-key, the new
313
+ directories are removed and the old skill remains intact. A context file that fails
314
+ to rebuild afterwards is reported and never undoes the rename.
254
315
 
255
316
  Proceed? [Y/N]
256
317
  ```
257
318
 
258
- **GATE [default: Y]** — If `{headless_mode}`: auto-proceed with [Y] and append `{gate: "confirm-rename", default_action: "proceed", taken_action: "proceed", reason: "headless auto-confirm"}` to `headless_decisions[]` (log line: "headless: auto-confirmed rename {old_name} → {new_name}"). Skip this append when `--dry-run` is set — a dry run does not execute the rename, so there is no confirmation to record.
319
+ **GATE [default: Y]**: If `{headless_mode}`: auto-proceed with [Y] (log line: "headless: auto-confirmed rename {old_name} → {new_name}") and record `{"gate": "confirm-rename", "default_action": "proceed", "taken_action": "proceed", "reason": "headless auto-confirm"}` in the run sink with §6's two commands, staged as `{run_dir}/decision.json`; when `record` exits non-zero, HALT (exit code 4, `halt_reason: "write-failed"`, `emit-halt` phase `select:confirm`) with its message. Skip the record when `--dry-run` is set: a dry run does not execute the rename, so there is no confirmation to record.
259
320
 
260
321
  Wait for explicit user response.
261
322
 
262
- **If `--dry-run` was passed**: skip the Y/N prompt entirely. Release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "**[DRY RUN] No changes were made — preview above shows what would be renamed.**", and emit the success envelope per SKILL.md "Result Contract (Headless)" with `status: "dry-run"`, the resolved `old_name`, `new_name`, `versions_renamed: {affected_versions}`, and `headless_decisions: {headless_decisions}` (carries the §6 source-authority override entry if it fired, else `[]`), then HALT (exit code 0). No copy, no manifest re-key, no delete.
323
+ **If `--dry-run` was passed**: skip the Y/N prompt entirely and display "**[DRY RUN] No changes were made: the preview above shows what would be renamed.**" The dry run took no lock (§4b), so it releases none. In `{headless_mode}`, emit the dry-run envelope on **stdout**. From `{project-root}`:
324
+
325
+ ```bash
326
+ cat > "{run_dir}/result-context.json" <<'SKF_RESULT'
327
+ {"status": "dry-run", "old_name": "{old_name}", "new_name": "{new_name}", "versions_renamed": <affected_versions as a JSON array>, "manifest_rekeyed": false, "context_files_updated": [], "halt_reason": null}
328
+ SKF_RESULT
329
+ uv run {emitEnvelopeHelper} emit --workflow skf-rename-skill --run-dir "{run_dir}" < "{run_dir}/result-context.json"
330
+ ```
331
+
332
+ Without `--result-dir` it writes no result file. It prints the complete line, `SKF_RENAME_SKILL_RESULT_JSON: {"status":"dry-run","old_name":"{old_name}","new_name":"{new_name}","versions_renamed":[<affected_versions>],"manifest_rekeyed":false,"context_files_updated":[],"exit_code":0,"halt_reason":null,"headless_decisions":[<the §6 source-authority decision, when it fired>],"run_id":"{run_id}","result_path":null}` (with `warnings` after `headless_decisions` when the run recorded one): display it verbatim. When it exits non-zero or prints no line, display its error, keep `{run_dir}` and say in one line that it holds the run's decisions, its warnings and the payload the emitter refused. Otherwise, in both modes, delete the run folder:
333
+
334
+ ```bash
335
+ rm -f "{run_dir}/result-context.json" "{run_dir}/decision.json" "{run_dir}/headless-decisions.jsonl" "{run_dir}/warnings.jsonl" && rmdir "{run_dir}"
336
+ ```
337
+
338
+ Then end the run with exit code 0: no copy, no manifest re-key, no delete.
263
339
 
264
340
  - **If `Y`** → proceed to section 9
265
- - **If `N`** (or `cancel` / `exit` / `[X]` / `:q`) → release the lock (`rm -f {forge_data_folder}/{old_name}/.skf-rename.lock`), display "**Cancelled.** No changes were made.", HALT (exit code 6, `halt_reason: "user-cancelled"`). In headless mode, emit the error envelope per SKILL.md "Result Contract (Headless)" with the resolved `old_name` and `new_name`.
341
+ - **If `N`** (or `cancel` / `exit` / `[X]` / `:q`) → display "**Cancelled.** No changes were made." and HALT (exit code 6, `halt_reason: "user-cancelled"`, `emit-halt` phase `select:confirm`).
266
342
  - **Any other input** → re-display the confirmation and ask again
267
343
 
268
344
  ### 9. Store Decisions in Context
269
345
 
270
346
  Store the following decisions in workflow context for step 2:
271
347
 
272
- - `old_name` — the current skill name
273
- - `new_name` — the validated new name
274
- - `affected_versions` — list of version strings for every version that must be updated
275
- - `affected_versions_count` — integer count of the above
276
- - `old_skill_group` — absolute path `{skills_output_folder}/{old_name}/`
277
- - `new_skill_group` — absolute path `{skills_output_folder}/{new_name}/`
278
- - `old_forge_group` — absolute path `{forge_data_folder}/{old_name}/`
279
- - `new_forge_group` — absolute path `{forge_data_folder}/{new_name}/`
280
- - `source_authority_override` — boolean (true if the user acknowledged the `"official"` warning, false/absent otherwise)
281
- - `headless_decisions` — the audit trail of confirmation gates auto-resolved under `{headless_mode}` (the §6 source-authority override and the §8 auto-confirm entries appended above). Initialize to `[]`; in interactive runs it stays `[]`. Step 2 carries it forward and step 3 surfaces it in the result envelope.
348
+ - `old_name`: the current skill name
349
+ - `new_name`: the validated new name
350
+ - `affected_versions`: every version §7 listed, the version folders step 2 copies; step 2 §2 renames the package of each one that holds it
351
+ - `old_skill_group`: absolute path `{skills_output_folder}/{old_name}` (no trailing `/`, like the three below)
352
+ - `new_skill_group`: absolute path `{skills_output_folder}/{new_name}`
353
+ - `old_forge_group`: absolute path `{forge_data_folder}/{old_name}`
354
+ - `new_forge_group`: absolute path `{forge_data_folder}/{new_name}`
355
+ - `forge_move`: boolean from §4a (true when this rename moves the forge folder)
356
+ - `forge_left_in_place`: the forge folder §4a leaves under the old name because SKF did not generate it, or null
357
+ - `same_folder`: boolean from §4a (true when `skills_output_folder` and `forge_data_folder` name one folder)
358
+ - `manifest_exists`: boolean from §2 (step 2 §6 re-keys the manifest only when it is true)
359
+ - `source_authority_override`: boolean (true if the user acknowledged the `"official"` warning, false/absent otherwise)
360
+ - `run_id` and `run_dir`: the run id and the run folder from §1
361
+ - `lock_owner`: the run lock's owner from §4b
362
+
363
+ The run's auto-decisions stay in `{run_dir}`, where each gate recorded them for the emitter.
282
364
 
283
365
  ### 10. Load Next Step
284
366