bmad-module-skill-forge 2.1.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 +51 -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 +14 -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 +117 -9
  75. package/src/shared/scripts/skf-detect-docs.py +892 -4
  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 +215 -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 +17 -6
  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 +55 -18
  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 -57
  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 -89
  348. package/src/skf-setup/references/detect-and-tier.md +58 -66
  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 -645
  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,6 +1,21 @@
1
1
  ---
2
2
  nextStepFile: 'structural-diff.md'
3
+ extractionPatternsData: 'skf-create-skill/references/extraction-patterns.md'
4
+ tierDegradationRulesData: 'skf-create-skill/references/tier-degradation-rules.md'
3
5
  outputFile: '{forge_version}/drift-report-{timestamp}.md'
6
+ # This run's stage data folder: the scan list, the recipe runner's JSON and
7
+ # the files of exports read by eye, which the snapshot is built from.
8
+ auditDataFolder: '{forge_version}/.skf-audit/{timestamp}'
9
+ extractPublicApiProbeOrder:
10
+ - '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
11
+ - '{project-root}/src/shared/scripts/skf-extract-public-api.py'
12
+ extractionSnapshotProbeOrder:
13
+ - '{project-root}/_bmad/skf/shared/scripts/skf-extraction-snapshot.py'
14
+ - '{project-root}/src/shared/scripts/skf-extraction-snapshot.py'
15
+ # Every HALT after the [C] of upstream-checkout.md closes the private tree with it.
16
+ sourceTreeProbeOrder:
17
+ - '{project-root}/_bmad/skf/shared/scripts/skf-source-tree.py'
18
+ - '{project-root}/src/shared/scripts/skf-source-tree.py'
4
19
  ---
5
20
 
6
21
  <!-- Config: communicate in {communication_language}. -->
@@ -14,164 +29,137 @@ Re-scan the source code using the current forge tier tools to build a fresh extr
14
29
  ## Rules
15
30
 
16
31
  - Focus only on extracting current source state — do not compare yet (that's Step 03)
17
- - Extract every file in the bounded scan list — a file skipped here makes step 3 flag its exports as false "removed" drift
18
- - Use subprocess Pattern 2 (per-file deep analysis) when available for AST extraction; if unavailable, extract in main thread file by file
32
+ - Extract every file in the bounded scan list: a file skipped here makes step 3 flag its exports as false "removed" drift. The snapshot helper gives each file a status and says whether the snapshot is complete (§3), so never judge completeness by eye
33
+ - At Forge, Forge+ and Deep the recipe runner extracts; read by eye only what it leaves (§3), using subprocess Pattern 2 (per-file deep analysis) when available to read those files; if unavailable, read them in the main thread file by file
19
34
 
20
35
  ## MANDATORY SEQUENCE
21
36
 
37
+ **Halt envelope.** Every HALT in this step names its exit code, `halt_reason` and phase. When `{source_tree}` is set (the [C] of `upstream-checkout.md`), first run `uv run {sourceTreeHelper} close --tree "{source_tree}"` from `{project-root}` and go on whatever it prints. In headless mode, stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "skill_name": "{skill_name}", "report_path": "{outputFile}"}`, adding `"path"` when the halt names one, then run:
38
+
39
+ ```bash
40
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-audit-skill --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
41
+ ```
42
+
43
+ Display the line it prints, then stop with the halt's exit code. If the emitter exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing.
44
+
22
45
  ### 1. Determine Extraction Strategy
23
46
 
47
+ Label every export by the tool that produced it, never by the tier: an export an ast-grep rule matched is T1 with `extraction_method: ast-grep` and, as `ast_node_type`, the `kind` the matching pattern or recipe declares in `{extractionPatternsData}` (ast-grep's output does not report it); an export read by eye is T1-low with `extraction_method: source-read` and `ast_node_type: null`.
48
+
24
49
  Based on forge tier detected in Step 01:
25
50
 
26
51
  **Quick tier (no AST tools):**
27
52
  - Read source files via gh_bridge or direct file I/O
28
53
  - Extract export names by text pattern matching (function/class/type declarations)
29
- - Confidence label: T1-low
54
+ - Label every export T1-low (read by eye, not matched by ast-grep) with `extraction_method: source-read` and `ast_node_type: null`
30
55
 
31
56
  **Forge tier (ast-grep available):**
32
- - Use ast_bridge to perform AST extraction per source file
33
- - Extract: export name, type (function/class/type/const), full signature, file path, line number
34
- - Confidence label: T1
57
+ - The recipe runner extracts, as the **AST Extraction Protocol** in `{extractionPatternsData}` says: one `skf-extract-public-api.py --mode full` call over the bounded scan list from §2 (§3 gives the command) runs every recipe for the language in one process, reads only the files on the list, and keeps every match (`--head-cap 0`: a match past a cap would read as a removed export in step 3). While it can run, never run the recipes one at a time, batch them, or merge and dedupe their matches by hand
58
+ - Each export it returns is T1 as it stands: its name (`$NAME`), type, file, line (`$NAME`'s line, as create-skill records it, not the match's first line, which a decorator or an `export` line can hold), `ast_node_type` and `signature`, the declaration on one line even when a formatter split its parameters over several
59
+ - Read by eye only what the runner leaves (§3): the forms the recipes leave out (Known Limitation #11 in `{extractionPatternsData}`) when a file uses them, the names an entry point exports that no recipe found, and the files it could not parse or read. Label each export by the tool that produced it: T1 with `extraction_method: ast-grep` for a runner export, T1-low with `extraction_method: source-read` and `ast_node_type: null` for an export read by eye
60
+
61
+ **Tier degradation handling (Forge/Forge+/Deep):** If the runner cannot run and ast-grep is unavailable, follow `{tierDegradationRulesData}` (AST Tool Unavailable, Per-File AST Failure) for the fallback and the user notification. A file ast-grep cannot parse falls back to reading that file only: its exports are T1-low with `extraction_method: source-read` and `ast_node_type: null`, and exports the runner matched in other files stay T1. Silent degradation is forbidden: the snapshot names each file's status, and the run names each file read after an ast-grep failure, and why (§3, §4); when ast-grep is unavailable for the whole run, every export is T1-low.
35
62
 
36
63
  **Forge+ tier (ast-grep + ccc available):**
37
- - Identical extraction to Forge tier: use ast_bridge for AST extraction per source file
38
- - Confidence label: T1
39
- - CCC rename detection available (see section 4b)
64
+ - Identical extraction to Forge tier (the AST Extraction Protocol, above)
65
+ - Label each export by tool, as at Forge tier
66
+ - CCC rename detection runs in step 3 (`structural-diff.md` §1b), over the exports the diff reports removed
40
67
 
41
68
  **Deep tier (ast-grep + QMD available):**
42
- - Forge extraction (above) PLUS
43
- - Query qmd_bridge for temporal context: when exports were added, modification history, usage frequency
44
- - Confidence labels: T1 for structural, T2 for temporal context
69
+ - Identical extraction to Forge tier, labeled by tool as at Forge tier
70
+ - No QMD query at this step: step 4 (semantic diff) queries QMD itself
45
71
 
46
- **Tool resolution:** `gh_bridge` → `gh api` commands or direct file I/O if local. `ast_bridge` → ast-grep MCP tools (`find_code`, `find_code_by_rule`) or `ast-grep` CLI. `qmd_bridge` → QMD MCP tools (`search`, `vector_search`) or `qmd` CLI. See `knowledge/tool-resolution.md`.
72
+ **Tool resolution:** `gh_bridge` → `gh api` commands or direct file I/O if local. `ast_bridge`, only when the runner cannot run (the protocol's **When the Runner Cannot Run**): the ast-grep MCP tool `find_code_by_rule` with a recipe and `output_format="json"`, or the protocol's CLI streaming template (`ast-grep scan -r {recipe_file} --json=stream`); `find_code` only as the fallback of Known Limitation #4. See `knowledge/tool-resolution.md`.
47
73
 
48
74
  ### 2. Build Bounded Scan List
49
75
 
50
- Audit-skill detects drift on files that were in scope during create-skill. The authoritative record of "what was in scope" is the provenance map loaded in step 1. Scan only those files — **audit-skill does NOT discover new files**. New-file detection is the responsibility of `skf-update-skill`, which maintains its own change manifest. To audit a project that has grown new files since creation, run update-skill first, then audit-skill.
76
+ Audit-skill detects drift on files that were in scope during create-skill. The authoritative record of "what was in scope" is the provenance map loaded in step 1. Scan only those files: **audit-skill does NOT discover new files**. New-file detection is the responsibility of `skf-update-skill`, which maintains its own change manifest; public API a package's entry points export from files outside that list reaches the drift report through the runner's entry-point diff (§3), for update-skill's scope reconciliation. To audit a project that has grown new files since creation, run update-skill first, then audit-skill.
51
77
 
52
78
  **Why bounded:** without this constraint, files that were deliberately excluded by the original brief's scope patterns (test fixtures, vendored code, generated artifacts, demo code, unrelated modules) get scanned on every audit and their exports are flagged by step 3 structural diff as "added" — false-positive drift that obscures real structural changes.
53
79
 
54
- **If a provenance map was loaded in step 1** (normal mode):
80
+ Every skill that reaches this step has a provenance map: step 1 stops a skill without one, and sends a compose-mode stack and a docs-only skill around this step.
55
81
 
56
- 1. The **bounded scan list** is `{bounded_scan_files}` — the union of `entries[].source_file` and `file_entries[].source_file`, deduplicated, sorted, and forward-slash normalized by init.md §4's `skf-load-provenance.py normalize` call. Consume it directly; do **not** re-walk the provenance map to rebuild it. The union/dedup/sort has one correct answer per map and is already scripted — re-deriving it in-prompt risks diverging from step 3, which diffs against the same normalized projection.
57
- 2. Verify each path under `{source_root}`. Files that existed at creation time but are now missing are **not** errors at this stage — keep them in the list so step 3 can classify them as DELETED. Handling missing files is step 3's job, not step 2's.
58
- 3. Record `bounded_scan: true` and `bounded_scan_source: "provenance-map"` in context for the evidence report.
59
- 4. Report:
82
+ 1. The **bounded scan list** is `{bounded_scan_files}`, the union of `entries[].source_file` and `file_entries[].source_file` that step 1 §4's `skf-load-provenance.py normalize` call returned. Do **not** re-walk the provenance map to rebuild it. Resolve `{extractionSnapshotHelper}` ← first existing path in `{extractionSnapshotProbeOrder}` (if none exists, HALT with **exit 3**, `halt_reason: "helper-missing"`, phase `re-index:scan-list`) and write the list where the runner reads it, from `{project-root}`:
60
83
 
61
- "**Bounded scan:** {count} files from provenance map ({provenance_date})."
62
-
63
- **If degraded mode** (no provenance map was loaded — user confirmed `[D]egraded mode` at step 1 §4):
64
-
65
- 1. Fall back to a source-tree scan: list all source files under `{source_root}` matching the project's primary language extensions (derive from `metadata.json.language` — e.g., `*.ts` / `*.tsx` for typescript, `*.py` for python, `*.rs` for rust, `*.go` for go).
66
- 2. Apply generic exclusions: `**/tests/**`, `**/test/**`, `**/__tests__/**`, `*.test.*`, `*.spec.*`, `node_modules/**`, `dist/**`, `build/**`, `target/**`, `__pycache__/**`, `.venv/**`, `vendor/**`.
67
- 3. Record `bounded_scan: false` and `bounded_scan_source: "source-tree-fallback"` in context.
68
- 4. Report:
84
+ ```bash
85
+ mkdir -p "{auditDataFolder}"
86
+ uv run {extractionSnapshotHelper} scan-list "{provenanceMap}" -o "{auditDataFolder}/scan-files.json"
87
+ ```
69
88
 
70
- "**Degraded mode scan:** {count} files from source tree (no provenance map — results may include files out of the original brief scope)."
89
+ It writes the same list as a JSON list of paths, the form the runner's `--files-from` reads: never retype it. If the folder or the file cannot be written (`status: "error"`), HALT with **exit 4**, `halt_reason: "write-failed"`, phase `re-index:scan-list`, `"path": "{auditDataFolder}"`.
90
+ 2. Files that existed at creation time but are now missing under `{source_root}` are **not** errors at this stage: keep them in the list. The snapshot marks them `missing`, and step 3 classifies their exports as DELETED.
91
+ 3. Report:
71
92
 
72
- **Count files to process** and proceed to section 3 with the resolved scan list.
93
+ "**Bounded scan:** {count} files from provenance map ({provenance_date})."
73
94
 
74
95
  ### 3. Extract Current Exports
75
96
 
76
- **For each file in the bounded scan list from §2, launch a subprocess that:**
77
- 1. Loads the source file
78
- 2. Extracts all public exports using tier-appropriate method
79
- 3. Records: export name, type, signature, file path, line number, confidence tier
80
- 4. Returns structured findings to parent
81
-
82
- **If a file from the bounded scan list is missing on disk:** record `{file, exports: [], status: "missing"}` and continue — step 3 structural diff will classify exports previously at this path as DELETED.
97
+ **1. The recipe runner** (Forge, Forge+ and Deep). Resolve `{extractPublicApiHelper}` ← first existing path in `{extractPublicApiProbeOrder}` and, from `{project-root}`, remove the JSON an earlier run left and run the runner over the scan list, so that a JSON at `-o` after the call is this call's:
83
98
 
84
- **If subprocess unavailable:** Perform extraction in main thread, processing each file sequentially.
85
-
86
- **Build extraction snapshot and persist it to `{forge_version}/extraction-snapshot.json`** — step 3 (`structural-diff.md`) reads this file directly, so it must be written to disk, not merely held in context:
87
- ```
88
- {
89
- "extraction_date": "{timestamp}",
90
- "confidence_tier": "{tier}",
91
- "source_root": "{source_path}",
92
- "files_scanned": {count},
93
- "bounded_scan": true|false,
94
- "bounded_scan_source": "provenance-map|source-tree-fallback",
95
- "exports": [
96
- {
97
- "name": "{export_name}",
98
- "type": "function|class|type|const|interface",
99
- "signature": "{full signature}",
100
- "file": "{relative_path}",
101
- "line": {line_number},
102
- "confidence": "T1|T1-low|T2"
103
- }
104
- ]
105
- }
99
+ ```bash
100
+ rm -f "{auditDataFolder}/extraction.json"
101
+ uv run {extractPublicApiHelper} --mode full \
102
+ --source-root "{source_root}" \
103
+ --files-from "{auditDataFolder}/scan-files.json" \
104
+ --head-cap 0 \
105
+ [--timeout "{runner_timeout}"] \
106
+ -o "{auditDataFolder}/extraction.json"
106
107
  ```
107
108
 
108
- Record the written path as `{extractionSnapshot}` in workflow context — step 3 passes it to the deterministic structural-diff helper.
109
-
110
- ### 4. Deep Tier Enhancement (Deep Only)
111
-
112
- **If forge tier is Deep:**
113
-
114
- Read the `qmd_collections` registry from `{sidecar_path}/forge-tier.yaml`.
109
+ The runner stops its ast-grep runs after 540 seconds and still writes its JSON (status `incomplete`), so give the command your shell tool's longest timeout; when that is under 10 minutes, pass `--timeout` as `{runner_timeout}`, a little under it (`100` under a two-minute limit). Never open the JSON: item 2's helper reads it and prints what this step acts on, its `status` included (never the exit code). At Quick tier no runner runs.
115
110
 
116
- Find the collection entry matching the current skill: look for an entry where `skill_name` matches the current skill being audited AND `type` is `"extraction"`.
111
+ **2. Build the snapshot.** Resolve `{extractionSnapshotHelper}` ← first existing path in `{extractionSnapshotProbeOrder}` (if none exists, HALT with **exit 3**, `halt_reason: "helper-missing"`, phase `re-index:snapshot`) and, from `{project-root}`, run:
117
112
 
118
- Three collection states must be handled distinctly — semantic-diff.md §2 branches the same way:
119
-
120
- **If a matching extraction collection is found and populated** (pre-query probe via `qmd ls {collection_name}` or equivalent returns one or more files):
121
- Query qmd_bridge against the `{skill_name}-extraction` collection for temporal context on each extracted export:
122
- - When was this export first added?
123
- - Has it been modified recently?
124
- - What is its usage frequency across the codebase?
125
- - How does the current extraction compare to the previously compiled skill content?
126
-
127
- Append temporal metadata to each export in the snapshot.
128
-
129
- **If a matching extraction collection is found but empty** (pre-query probe reports `Files: 0 (updated never)` or an empty listing):
130
- Log: "QMD collection `{collection_name}` is registered but empty. Run `qmd update` to (re-)index `{collection.path}`, then re-audit. Temporal enrichment skipped for this run."
131
- Continue without T2 enrichment — the unpopulated collection is a setup gap, not an extraction failure. Step-04 will fall through to its direct-content fallback for semantic diff.
132
-
133
- **If no matching collection found in registry:**
134
- Log: "No QMD extraction collection found for {skill_name}. Temporal enrichment skipped. Re-run [CS] Create Skill to generate the collection."
135
- Continue without T2 enrichment — this is not an error.
113
+ ```bash
114
+ uv run {extractionSnapshotHelper} build \
115
+ --source-root "{source_root}" \
116
+ --source-path "{source_path}" \
117
+ --tier "{tier}" \
118
+ --date "{timestamp}" \
119
+ --provenance-map "{provenanceMap}" \
120
+ [--extraction "{auditDataFolder}/extraction.json"] \
121
+ [--details "{auditDataFolder}/export-details-{n}.json"]... \
122
+ -o "{forge_version}/extraction-snapshot.json"
123
+ ```
136
124
 
137
- **If forge tier is Quick, Forge, or Forge+:**
138
- Skip this section. Temporal context requires Deep tier.
125
+ Pass `--extraction` when the runner wrote its JSON, and one `--details` for each file item 3 wrote (none on the first build). The helper merges the runner's exports with the ones read by eye, gives every file on the list one status (`extracted`, `read-by-eye`, `hash-tracked` for a script, asset or doc only `file_entries[]` names, which step 3 compares by content hash, `missing`, `parse-failed` or `unread`) and prints one line. Act on its fields:
139
126
 
140
- ### 4b. CCC Rename Detection (Forge+ and Deep with ccc)
127
+ - **`runner_status` is `incomplete`** on the first build (an ast-grep run failed, or the time limit ran out): run item 1 once more, then this command again.
128
+ - **`runner_status` is `no-ast-grep`, or null at Forge tier and above** (no JSON at `-o` after the call, or no runner candidate): the runner cannot run. Follow **When the Runner Cannot Run** in `{extractionPatternsData}` over the files in `to_read`, with this audit's parameters in place of create-skill's: the files in scope are those `to_read` lists, and their count is the Decision Tree's input (not create-skill's step 3 count). Keep every match, in every directory batch and every retry: pass `find_code_by_rule` a `max_results` that no recipe can reach (100000), and a Known Limitation #4 `find_code` retry the same, and drop the `| head -{HEAD_CAP}` from the CLI streaming template, in each batch, since a match past a cap reads as a removed export in step 3. Record each export as item 3 does, T1 with `ast-grep` for one the recipes match.
129
+ - **`to_read` or `extraction_gaps` is not empty**, or a file uses the forms the recipes leave out: item 3 reads them, then run this command again with its details. When `complete` is still false, the files `to_read` lists could not be read: HALT with **exit 3**, `halt_reason: "source-unreadable"`, phase `re-index:snapshot`, naming them. Step 3 never reads an incomplete snapshot.
130
+ - **`complete` is true** and nothing is left to read: go on to §4.
131
+ - **`status: "error"`:** an `error` that starts `cannot write` is a failed write: HALT with **exit 4**, `halt_reason: "write-failed"`, phase `re-index:snapshot`, `"path": "{forge_version}/extraction-snapshot.json"`. Any other names a file this step wrote: fix it and run the command again.
132
+ - **The command fails otherwise, or prints no JSON:** HALT with **exit 3**, `halt_reason: "helper-missing"`, phase `re-index:snapshot`, showing the first stderr line.
141
133
 
142
- **If `tools.ccc` is true in forge-tier.yaml:**
134
+ **3. What the runner leaves.** Read by eye (T1-low, `extraction_method: source-read`, `ast_node_type: null`) only:
143
135
 
144
- For each export in the skill baseline that was NOT found at its recorded file path during re-extraction (potential "deleted" export):
136
+ - each file in `to_read`: a `parse-failed` one (its `issue`: a syntax error, where a recipe can miss an export; not UTF-8; unreadable), with the warning "**AST fallback:** ast-grep could not parse {file} ({issue}); its exports were read from source and labeled T1-low.", and an `unread` one (an incomplete run, or no runner: then every file, by text pattern matching at Quick tier);
137
+ - each name in `extraction_gaps`, in its `file` (an entry point exports it, no recipe found it);
138
+ - the forms the recipes leave out (Known Limitation #11 in `{extractionPatternsData}`) in a file that uses them.
145
139
 
146
- 1. Run `ccc_bridge.search("{export_name}", source_root, top_k=5)` — **Tool resolution:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `cd {source_root} && ccc search --limit 5 "{export_name}"` (CLI) — to find candidate current locations. `ccc search` reads the index in the current working directory and has no project-selector flag (`--path` is a file-path glob filter *within* the index, and the result cap is `--limit`, not `--top`) — see `knowledge/ccc-bridge.md`.
147
- 2. If CCC returns files containing the export name:
148
- - Run ast-grep verification on each candidate file
149
- - If verified at a new location: reclassify from "deleted" to "moved" with the new file:line reference
150
- - This reduces false-positive structural drift findings where exports were relocated, not removed
151
- 3. If CCC returns no results or verification fails: keep the "deleted" classification
140
+ For each file to read, launch a subprocess (Pattern 2) that loads it and returns ONLY `{"files": [...], "exports": [...]}`, with no prose and no fences: `files` lists the file when the worker read the whole of it (also when it holds no export), and each export gives `name`, `type`, `signature` (the declaration on one line), `file` (relative to `{source_root}`, with forward slashes), `line` (`$NAME`'s line), `confidence: "T1-low"`, `extraction_method: "source-read"` and `ast_node_type: null`. Save each worker's JSON as it returned it, as `{auditDataFolder}/export-details-{n}.json` (`{n}` counting from 1), never merged by hand; without subprocesses, read the files in the main thread, one by one, and save one such file. A stack's export needs no library: the snapshot helper gives each one its file's `source_library` from the provenance map (the map `normalize` returns as `source_library_by_file`).
152
141
 
153
- CCC failures: skip rename detection silently, proceed with standard structural diff.
142
+ **The snapshot.** The command writes `{forge_version}/extraction-snapshot.json`, which step 3 (`structural-diff.md`) reads directly: `exports[]` (for a stack, each with its file's `source_library`), `files[]` with each file's status, `files_by_status`, `counts` and `cap_hits` (§4) and `outside_scope` (step 6's Out-of-Scope New Public API table); `skf-extraction-snapshot.py --help` gives the full shape. Record the written path as `{extractionSnapshot}` in workflow context: step 3 passes it to the structural-diff helper.
154
143
 
155
- **If `tools.ccc` is false:** Skip this section silently.
144
+ ### 4. Validate Extraction Completeness
156
145
 
157
- ### 5. Validate Extraction Completeness
146
+ Take every value from the snapshot (`files_by_status`, `counts`, `cap_hits`) and the build's line (`ast_fallback_files`), never a recount:
158
147
 
159
148
  "**Extraction complete.**
160
149
 
161
150
  | Metric | Value |
162
151
  |--------|-------|
163
- | Scan mode | {bounded (provenance-map) / degraded (source-tree)} |
164
- | Files scanned | {count} |
165
- | Exports found | {total_exports} |
166
- | Functions | {function_count} |
167
- | Classes | {class_count} |
168
- | Types/Interfaces | {type_count} |
169
- | Constants | {const_count} |
170
- | Confidence | {T1/T1-low/T2} |
152
+ | Files scanned | {files_scanned}: {extracted} extracted, {read-by-eye} read by eye, {hash-tracked} tracked by hash, {missing} missing |
153
+ | Exports found | {counts.exports} |
154
+ | By type | {each counts.by_type entry as `{type}` ×{count}} |
155
+ | Labels | {t1_count} T1 (`ast-grep`), {t1_low_count} T1-low (`source-read`) |
156
+ | AST fallback files | {count and names from `ast_fallback_files`, or none; n/a at Quick} |
157
+ | Cap hits | {cap_hits, or none} |
171
158
 
172
159
  **Proceeding to structural comparison...**"
173
160
 
174
- ### 6. Update Report and Auto-Proceed
161
+ `ast_fallback_files` names the files read by eye after the runner could not parse or read them, not one read again for a gap or a Known Limitation #11 form (`all files (ast-grep unavailable)` when the runner could not run, none at Quick tier).
175
162
 
176
- Update {outputFile} frontmatter — append `'re-index'` to `stepsCompleted`. Once the extraction snapshot is complete with all source files processed, load, read fully, and execute `{nextStepFile}` (structural diff).
163
+ ### 5. Update Report and Auto-Proceed
177
164
 
165
+ Update {outputFile} frontmatter: append `'re-index'` to `stepsCompleted` and set `ast_fallback_files` to the last build's `ast_fallback_files`. Once the snapshot helper reports `complete: true`, load, read fully, and execute `{nextStepFile}` (structural diff).
@@ -1,6 +1,17 @@
1
1
  ---
2
2
  outputFile: '{forge_version}/drift-report-{timestamp}.md'
3
3
  nextStepFile: 'health-check.md'
4
+ # This run's stage data folder, where step 5 saved the findings and their
5
+ # classification.
6
+ auditDataFolder: '{forge_version}/.skf-audit/{timestamp}'
7
+ # Resolve `{sourceTreeHelper}` to the first existing path. The Result
8
+ # Contract removes the private tree the [C] of upstream-checkout.md read
9
+ # the source in.
10
+ sourceTreeProbeOrder:
11
+ - '{project-root}/_bmad/skf/shared/scripts/skf-source-tree.py'
12
+ - '{project-root}/src/shared/scripts/skf-source-tree.py'
13
+ # §2: this skill's renderer of the Out-of-Scope New Public API table, from the skill root.
14
+ renderDriftTablesScript: 'scripts/render-drift-tables.py'
4
15
  ---
5
16
 
6
17
  <!-- Config: communicate in {communication_language}. Drift report prose in {document_output_language}. -->
@@ -20,21 +31,38 @@ Finalize the drift report by completing the Audit Summary with calculated metric
20
31
 
21
32
  ## MANDATORY SEQUENCE
22
33
 
34
+ **Halt envelope.** Every HALT in this step names its exit code, `halt_reason` and phase. When `{source_tree}` is set (the [C] of `upstream-checkout.md`), first run `uv run {sourceTreeHelper} close --tree "{source_tree}"` from `{project-root}` and go on whatever it prints. In headless mode, stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "skill_name": "{skill_name}", "report_path": "{outputFile}", "drift_score": "<the saved classification's drift_score>"}`, adding `"path"` when the halt names one, then run:
35
+
36
+ ```bash
37
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-audit-skill --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
38
+ ```
39
+
40
+ Display the line it prints, then stop with the halt's exit code. If the emitter exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing.
41
+
23
42
  ### 1. Complete Audit Summary
24
43
 
25
44
  Update the ## Audit Summary section at the top of {outputFile} with final calculated values:
26
45
 
27
- - Fill in severity count table from Step 05 classification summary
28
- - Set overall drift score
29
- - Add total findings count
46
+ - Fill in the severity count table from `{auditDataFolder}/severity.json`, the classification step 5 saved: `by_severity` for each level and `total_items` for **Total**
47
+ - Set the overall drift score (its `drift_score`)
30
48
  - Include doc drift summary from `doc_drift_summary` context (set by step 5a):
31
- - If `changed > 0`: "**Doc Drift:** {changed} of {total_tracked} tracked doc(s) have changed since compile. Consider re-running CS to update doc_sources."
49
+ - If `changed > 0`: "**Doc Drift:** {changed} of {total_tracked} tracked doc(s) have changed since compile." Then, for a docs-only skill (whose score these changes set): "Run `[US] Update Skill` to bring the skill up to them."; otherwise: "Consider re-running CS to update doc_sources."
32
50
  - If `fetch_failed > 0`: "{fetch_failed} doc URL(s) could not be reached during audit."
33
51
  - If `skipped_entirely`: no mention in summary (already noted in the doc drift section)
34
52
 
35
53
  ### 2. Generate Remediation Suggestions
36
54
 
37
- For each classified drift finding, write one concrete remediation derived from the finding itself: **what** to change in the audited **SKILL.md** (or its `references/`) — not the source code — **where** (the section plus the source `{file}:{line}` the finding cites), and **why**. Set effort (`low`/`medium`/`high`) by how much of the skill doc the change touches. A reviewer should be able to act on each row without re-deriving the finding.
55
+ For each classified drift finding (`findings[]` of `{auditDataFolder}/severity.json`; one remediation may cover a rollup row of step 5's tables), write one concrete remediation derived from the finding itself: **what** to change in the audited **SKILL.md** (or its `references/`), not the source code; **where** (the section plus the source `{file}:{line}` the finding cites); and **why**. Set effort (`low`/`medium`/`high`) by how much of the skill doc the change touches. A reviewer should be able to act on each row without re-deriving the finding.
56
+
57
+ Rows of step 3's **Provenance label differences (not drift)** table are not findings: they get no remediation row and do not count toward the Workflow Recommendation.
58
+
59
+ **Public API outside the skill's scope.** Step 2's snapshot lists in `outside_scope` the files the skill does not cover whose public API a package entry point exports: the recipe runner's entry-point diff found them, so nothing here is judged by eye. Render their subsection from the snapshot, never by hand. `{renderDriftTablesScript}` resolves relative to the skill root; from `{project-root}`, run:
60
+
61
+ ```bash
62
+ uv run {renderDriftTablesScript} outside-scope "{forge_version}/extraction-snapshot.json"
63
+ ```
64
+
65
+ Paste its output unchanged under Remediation Suggestions, where the template below shows it (nothing when it prints nothing: `outside_scope` is empty, as at Quick tier). update-skill's scope reconciliation (`skf-provenance-gap-dispatch.py`) reads that subsection from the newest drift report and asks whether to bring each path into scope. A compose-mode stack and a docs-only skill have no snapshot: skip the command. When the command exits non-zero, its JSON `error`, else its first stderr line, says what it could not read: write `Out-of-Scope New Public API not rendered: {error}` in place of its output and go on.
38
66
 
39
67
  Append to {outputFile}:
40
68
 
@@ -59,49 +87,61 @@ Append to {outputFile}:
59
87
  |---|---------|-------------|
60
88
  | 1 | {finding} | {specific action} |
61
89
 
90
+ {what the outside-scope command printed, unchanged}
91
+
62
92
  ### Workflow Recommendation
63
93
 
64
94
  {IF any CRITICAL or HIGH findings:}
65
95
  **Recommended:** Run `[US] Update Skill` workflow to apply priority remediations automatically.
66
96
 
67
- {IF `audit_ref != baseline_ref` (source version bump detected in step 1 §5b):}
68
- **Version preservation (non-destructive).** `update-skill` preserves the prior version at `{skill_group}/{baseline_version}/` unchanged and writes the new skill to `{skill_group}/{audit_version}/` (see `skf-update-skill/references/merge.md` §6b, which creates the new version directory and leaves the previous one on disk). The `active` symlink at `{skill_group}/active` repoints to the new version (see `skf-update-skill/references/write.md` §5b). On the next export, the prior version's export-manifest entry transitions to `status: archived` — files retained for rollback (see `skf-export-skill/references/update-context.md`). Do **not** recommend `skf-drop-skill` + `skf-create-skill` for a version bump — that destroys the prior version's artifacts.
97
+ {IF the frontmatter's `upstream_moved` is true:}
98
+ **Upstream moved.** The skill is built from `{baseline_ref}` and upstream is now at `{upstream_ref}`{; this audit read `{upstream_ref}`, when `audit_ref_source` is `checkout-latest`; this audit read the baseline, otherwise}. Run `[US] Update Skill` with `--target-ref {upstream_ref}`, so the update compares the ref upstream moved to rather than the one the skill is pinned to.
99
+
100
+ {IF the frontmatter's `audit_ref_source` is `checkout-latest` (the [C] of `upstream-checkout.md` audited a newer upstream ref than the baseline) or its `upstream_moved` is true:}
101
+ **Version preservation (non-destructive).** `update-skill` keeps the audited version (`{audited_version}`) unchanged, writes the new version in a folder of its own beside it (see `skf-update-skill/references/merge.md` §6b, which leaves the previous version on disk) and repoints the skill's `active` link at it (see `skf-update-skill/references/write.md` §5b). On the next export, the prior version's export-manifest entry transitions to `status: archived`, its files kept for rollback (see `skf-export-skill/references/update-context.md`). Do **not** recommend `skf-drop-skill` + `skf-create-skill` for a version bump: that destroys the prior version's artifacts.
69
102
 
70
- **Surface new entry points for the brief gate.** If the audit observed new top-level modules, renamed package trees, or new public entry points (new `__init__.py`, `index.ts`, `lib.rs`, or equivalent) that were not in the brief's original scope, call them out here. `update-skill` step 2 §1b detects new candidate files via heuristic and prompts `[P]romote` / `[S]kip` / `[U]pdate-brief`; surfacing them in advance makes that gate faster to resolve, or lets the user refine scope via `skf-brief-skill` before running update-skill.
103
+ {IF the Out-of-Scope New Public API table above has rows:}
104
+ **New public API outside the scope.** update-skill's scope reconciliation (detect-changes §1c) offers each path to bring into the brief's scope; review them there, or refine the scope with `skf-brief-skill` before running update-skill.
71
105
 
72
106
  {IF only MEDIUM or LOW findings:}
73
107
  **Optional:** Minor drift detected. Manual updates sufficient, or run `[US] Update Skill` for automated remediation.
74
108
 
75
109
  {IF CLEAN:}
76
- **No action needed.** Skill is current with source code.
110
+ **No drift against `{audit_ref}`.** The skill matches the source at `{audit_ref}` (`{audit_commit_short}`){; upstream has not moved past `{baseline_ref}`, when `upstream_moved` is false; upstream was not checked ({upstream_fetch}), when it is null; and when it is true, the Upstream moved recommendation above applies}.
77
111
  ```
78
112
 
79
113
  ### 3. Add Provenance Section
80
114
 
81
- Append to {outputFile}:
115
+ Build the section from the frontmatter of {outputFile}, where step 1 §6, step 2 §5 and step 3 §6 recorded each value as it was known, not from values held in the conversation. Append to {outputFile}:
82
116
 
83
117
  ```markdown
84
118
  ## Provenance
85
119
 
86
120
  | Field | Value |
87
121
  |-------|-------|
88
- | **Audit Date** | {current_date} |
122
+ | **Audit Date** | {date} |
89
123
  | **Audited By** | Ferris (Audit mode) |
90
- | **Forge Tier** | {tier} |
91
- | **Tools Used** | {tool_list based on tier} |
92
- | **Source Path** | {source_path} |
124
+ | **Forge Tier** | {forge_tier} |
125
+ | **Tools Used** | {tool_list based on forge_tier} |
126
+ | **Source Path** | {source_path}{; read from the private tree `{source_tree}`, when set} |
93
127
  | **Skill Path** | {skill_path} |
94
- | **Provenance Map** | {provenance_map_path} |
95
- | **Provenance Age** | {days} days |
96
- | **Mode** | {normal / degraded} |
128
+ | **Audited Version** | `{audited_version}` ({audited_version_reason}){; the export manifest names `{manifest_version}`, when set} |
129
+ | **Provenance Map** | {provenance_map} |
130
+ | **Provenance Age** | {provenance_age_days} days (generated {provenance_generated_at}) |
131
+ | **AST fallback files** | {`ast_fallback_files`, or none; n/a at Quick} |
132
+ | **Applied Transforms** | {each `applied_transforms` entry as `{transform}` ×{count}, or none} |
97
133
  | **Baseline Ref / Commit** | `{baseline_ref}` @ `{baseline_commit_short}` |
98
134
  | **Audit Ref / Commit** | `{audit_ref}` @ `{audit_commit_short}` ({audit_ref_source}) |
99
- | **Upstream Latest** | `{latest_tag or remote_head or "(not fetched)"}` |
135
+ | **Upstream Latest** | `{latest_tag or remote_head or "(not fetched)"}` (fetch: {upstream_fetch}) |
136
+ | **Upstream Moved** | {upstream_moved}{: to `{upstream_ref}`, when true} |
137
+ | **Stage Data** | `{auditDataFolder}/`: the JSON this run saved and classified (the structural diff, the constituents' freshness or the documents' comparison, the findings and their classification) |
100
138
 
101
139
  **Confidence Legend:**
102
- - **T1:** AST extraction — high reliability, structural truth
103
- - **T1-low:** Text pattern matching — moderate reliability
104
- - **T2:** QMD temporal context — evidence-backed semantic analysis
140
+ - **T1:** an ast-grep match (`extraction_method: ast-grep`) at any tier: high reliability, structural truth
141
+ - **T1-low:** read by eye (`extraction_method: source-read`) at any tier: moderate reliability
142
+ - **T1-low-fallback:** Deep-tier semantic diff read directly from the skill's docs and the current source because the QMD collection was empty: moderate reliability
143
+ - **T2:** Deep-tier semantic diff: a claim the skill's QMD collection holds, checked against the current source line it cites: evidence-backed semantic analysis
144
+ - **T3:** external documentation reference: variable reliability, secondary source
105
145
  ```
106
146
 
107
147
  ### 4. Update Report Frontmatter
@@ -109,43 +149,66 @@ Append to {outputFile}:
109
149
  Update {outputFile} frontmatter:
110
150
  - Append `'report'` to `stepsCompleted`
111
151
  - Set `drift_score` to the score from step 5's classification helper
112
- - Set `nextWorkflow` to `'update-skill'` if CRITICAL or HIGH findings, otherwise leave empty
152
+ - Set `nextWorkflow` to `'update-skill'` when the saved classification's `by_severity.CRITICAL` or `by_severity.HIGH` is above 0, or when the frontmatter's `upstream_moved` is true, otherwise leave it empty
113
153
 
114
- If finalizing the report or writing the result JSON below fails (read-only mount, disk full, permissions denied) → HALT with **exit 4**, `halt_reason: "write-failed"`. When `{headless_mode}`, emit the error envelope on **stderr** (shape per SKILL.md → Result Contract) with `report_path: null`.
154
+ If finalizing the report fails (read-only mount, disk full, permissions denied) → HALT with **exit 4**, `halt_reason: "write-failed"`, phase `report:write`, `"path": "{outputFile}"`, and no `report_path` in the payload.
115
155
 
116
156
  ### 5. Present Final Report Summary
117
157
 
118
158
  Present a concise completion summary to the user conveying: the skill name, the **overall drift score** (CLEAN / MINOR / SIGNIFICANT / CRITICAL), the severity-count table (CRITICAL / HIGH / MEDIUM / LOW / Total), and the saved report path (`{outputFile}`). Close with the next-action recommendation matching the drift level:
119
159
 
120
160
  - **CRITICAL or HIGH findings** → action required: recommend running the `[US] Update Skill` workflow to apply the priority remediations; manual review at `{outputFile}` is the alternative.
161
+ - **The frontmatter's `upstream_moved` is true**, whatever the drift score → recommend `[US] Update Skill` with `--target-ref {upstream_ref}`, naming `{baseline_ref}` and `{upstream_ref}`: update-skill otherwise compares the ref the skill is pinned to, not the one upstream moved to. `{upstream_ref}` is the newer tag when upstream released one (`--target-ref {latest_tag}`), and for a skill built from the default branch it is `HEAD` (`--target-ref HEAD`).
121
162
  - **MEDIUM or LOW only** → minor drift: manual updates suffice, or run `[US] Update Skill` for automated remediation.
122
- - **CLEAN** → no action needed; the skill is current and ready for `[EX] Export Skill`.
163
+ - **CLEAN, and `upstream_moved` is not true** → no action needed: the skill matches `{audit_ref}` and is ready for `[EX] Export Skill`.
123
164
 
124
165
  This summary reads as final but is **not** the terminal step — proceed to §6.
125
166
 
126
167
  ### Result Contract
127
168
 
128
- Write the result contract per `shared/references/output-contract-schema.md`: the per-run record at `{forge_version}/audit-skill-result-{YYYYMMDD-HHmmss}.json` (UTC timestamp, resolution to seconds) and a copy at `{forge_version}/audit-skill-result-latest.json` (stable path for pipeline consumers — copy, not symlink). Include the drift report path in `outputs`; include `drift_count` and `severity` (CLEAN/MINOR/SIGNIFICANT/CRITICAL) in `summary`.
169
+ **Remove the private source tree first.** When `{source_tree}` is set (the [C] of `upstream-checkout.md` read the source there), no later step reads the source: resolve `{sourceTreeHelper}` ← first existing path in `{sourceTreeProbeOrder}` and, from `{project-root}`, run `uv run {sourceTreeHelper} close --tree "{source_tree}"`. Go on whatever it prints. On `left` or `refused`, a command that fails or prints no JSON, or no candidate, record `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "source_tree_not_removed: {source_tree}"` (a later SKF run removes it once it is seven days old). Then set `{source_tree}` to null.
170
+
171
+ The shared emitter writes the result contract (per `shared/references/output-contract-schema.md`, which resolves relative to the SKF module root: `{project-root}/_bmad/skf/` when installed, `{project-root}/src/` during development) and prints the `SKF_AUDIT_RESULT_JSON` envelope; this step stages their content, every value from the drift report's frontmatter and the classification step 5 saved. Write `{run_dir}/result-context.json`:
172
+
173
+ ```json
174
+ {
175
+ "status": "success",
176
+ "skill_name": "{skill_name}",
177
+ "drift_score": "<drift_score>",
178
+ "report_path": "{outputFile}",
179
+ "next_workflow": <next_workflow>,
180
+ "audit_ref": "<audit_ref>",
181
+ "upstream_moved": <upstream_moved>,
182
+ "upstream_ref": <upstream_ref>,
183
+ "result_contract": {
184
+ "skill": "skf-audit-skill",
185
+ "status": "success",
186
+ "outputs": [{"type": "report", "path": "{outputFile}"}],
187
+ "summary": {"drift_count": <total_findings>, "severity": "<drift_score>", "next_workflow": <next_workflow>,
188
+ "audit_ref": "<audit_ref>", "upstream_moved": <upstream_moved>, "upstream_ref": <upstream_ref>}
189
+ }
190
+ }
191
+ ```
129
192
 
130
- **Stdout envelope (headless only).** When `{headless_mode}` is true, emit a single-line JSON envelope to **stdout** immediately after the on-disk result contract is written, so chaining workflows can consume `drift_score`, `report_path`, and `next_workflow` from a captured stdout line without polling the filesystem. The shape matches the "Result Contract (Headless)" section in SKILL.md verbatim:
193
+ When On Activation step 3 fell back to the bundled `customize.toml`, add `"customization_resolver_unavailable": "<the reason>"`, the one-line reason On Activation kept with each `"` and `\` escaped: the emitter turns it into a warning. `<drift_score>` and `<total_findings>` are the `drift_score` and `total_findings` of `{auditDataFolder}/severity.json`: `drift_count` (the saved classification's `total_findings`: one per finding, never a label difference) and `severity` (its `drift_score`: CLEAN/MINOR/SIGNIFICANT/CRITICAL). `<next_workflow>` is `"update-skill"` exactly when §4 set the frontmatter's `nextWorkflow` (CRITICAL or HIGH findings, or `upstream_moved` true), else JSON `null`. `audit_ref`, `upstream_moved` and `upstream_ref` are the frontmatter's, resolved at step 1 §5b and, when upstream moved, by `upstream-checkout.md` (`audit_ref` is `baseline_ref` when the audit stayed on the baseline, `upstream_ref` when the operator chose `[C]`): `<upstream_moved>` is JSON `true` when upstream moved, `false` when the check found it unchanged and `null` when the check was skipped or failed, and `<upstream_ref>` is the ref upstream moved to, as a JSON string, when `upstream_moved` is true, else `null`. Then run, in every mode:
131
194
 
132
- ```
133
- SKF_AUDIT_RESULT_JSON: {"status":"success","skill_name":"{skill_name}","drift_score":"{CLEAN|MINOR|SIGNIFICANT|CRITICAL}","report_path":"{outputFile}","next_workflow":"{update-skill|null}","audit_ref":"{audit_ref}","exit_code":0,"halt_reason":null}
195
+ ```bash
196
+ uv run {emitEnvelopeHelper} emit --workflow skf-audit-skill --run-dir "{run_dir}" --result-dir "{forge_version}" < "{run_dir}/result-context.json"
134
197
  ```
135
198
 
136
- Field rules: `next_workflow` is `"update-skill"` when CRITICAL or HIGH findings exist (matches the frontmatter `nextWorkflow` set in §4), otherwise `null`. `audit_ref` carries the resolved value from step 1 §5b (`baseline_ref` when no upstream drift was detected, `latest_tag` or `remote_head` when the operator chose `[C] Checkout-and-audit-against-latest`).
199
+ It writes `{forge_version}/audit-skill-result-{YYYYMMDD-HHmmss}.json` (UTC; it picks the name) and its `audit-skill-result-latest.json` copy (stable path for pipeline consumers: a copy, not a symlink), stamps the timestamp, `run_id`, `headless_decisions` and `warnings` into both, and prints the `SKF_AUDIT_RESULT_JSON:` line on stdout. When `{headless_mode}` is true, display the line verbatim: chaining workflows read `drift_score`, `report_path`, `next_workflow`, `upstream_moved` and `upstream_ref` from it without polling the filesystem. A result file that could not be written leaves `result_path` null and a `result_file_write_failed` warning in the line, and the run still finishes. If the emitter exits non-zero, correct `result-context.json` from the message on its stderr and run it once more; if it fails again, HALT with **exit 4**, `halt_reason: "write-failed"`, phase `report:result-contract`: the run's result contract could not be written.
137
200
 
138
- **Hard-halt envelope (headless only).** Every hard halt emits this same envelope shape on **stderr** with `status: "error"` and the `exit_code` / `halt_reason` for its failure class (per SKILL.md → Exit Codes and Result Contract), produced at the halting site before exit — it is the only failure signal a wrapping pipeline receives, so log it before exiting. `drift_score` carries its last known value (`null` if classification never ran); `report_path` is `null` when the report write failed.
201
+ **Hard-halt envelope (headless only).** Every hard halt prints its envelope through the emitter on **stderr** with `status: "error"` and the `exit_code` / `halt_reason` for its failure class, as each step's **Halt envelope** paragraph shows: it is the only failure signal a wrapping pipeline receives. `drift_score` carries its last known value (`null` if classification never ran); `report_path` is `null` when the report write failed.
139
202
 
140
- **Post-audit hook (optional).** If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it as:
203
+ **Post-audit hook (optional).** If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it from `{project-root}` as:
141
204
 
142
205
  ```bash
143
206
  {onCompleteCommand} --result-path={result_json_path}
144
207
  ```
145
208
 
146
- where `{result_json_path}` is the per-run record path written above (`{forge_version}/audit-skill-result-{YYYYMMDD-HHmmss}.json`). Log success/failure to `workflow_warnings[]` — never fail the workflow on a hook error. The hook runs after the result contract is finalized so notifiers, ticket-tracker integrations, or downstream pipelines see a complete record. When `{onCompleteCommand}` is empty (bundled default), skip the invocation entirely.
209
+ where `{result_json_path}` is the envelope's `result_path`, the per-run record the emitter wrote (skip the hook when it is null). When the hook fails, tell the user it failed and why, and go on: never fail the workflow on a hook error. The hook runs after the result contract is finalized so notifiers, ticket-tracker integrations, or downstream pipelines see a complete record. When `{onCompleteCommand}` is empty (bundled default), skip the invocation entirely.
147
210
 
148
211
  ### 6. Chain to Health Check
149
212
 
150
- Only when the report has been written, presented, and the result contract saved do you then load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the user-facing summary reads as final.
213
+ Only when the report has been written, presented, and the result contract saved, delete the run folder, whose decisions, warnings and payloads the run no longer needs: `rm -rf "{run_dir}"`. Then load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step: do not stop here even though the user-facing summary reads as final.
151
214