bmad-module-skill-forge 2.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +49 -49
  3. package/docs/404.md +10 -8
  4. package/docs/_data/pinned.yaml +22 -13
  5. package/docs/_internal/STABILITY.md +58 -35
  6. package/docs/agents.md +39 -53
  7. package/docs/architecture.md +65 -51
  8. package/docs/bmad-synergy.md +53 -51
  9. package/docs/campaign.md +127 -75
  10. package/docs/concepts.md +34 -28
  11. package/docs/examples.md +70 -68
  12. package/docs/forge-auto.md +34 -31
  13. package/docs/getting-started.md +118 -57
  14. package/docs/how-it-works.md +25 -21
  15. package/docs/index.md +15 -15
  16. package/docs/skill-model.md +70 -59
  17. package/docs/troubleshooting.md +143 -18
  18. package/docs/verifying-a-skill.md +87 -57
  19. package/docs/why-skf.md +24 -22
  20. package/docs/workflows.md +274 -110
  21. package/package.json +18 -10
  22. package/src/forger/preferences.yaml +8 -1
  23. package/src/knowledge/agentskills-spec.md +2 -0
  24. package/src/knowledge/ccc-bridge.md +40 -24
  25. package/src/knowledge/confidence-tiers.md +9 -5
  26. package/src/knowledge/overview.md +3 -3
  27. package/src/knowledge/progressive-capability.md +8 -8
  28. package/src/knowledge/provenance-tracking.md +9 -5
  29. package/src/knowledge/qmd-registry.md +3 -3
  30. package/src/knowledge/skf-knowledge-index.csv +2 -2
  31. package/src/knowledge/skill-lifecycle.md +18 -11
  32. package/src/knowledge/split-body-strategy.md +0 -3
  33. package/src/knowledge/tool-resolution.md +4 -2
  34. package/src/knowledge/version-paths.md +101 -15
  35. package/src/module-help.csv +3 -3
  36. package/src/module.yaml +10 -0
  37. package/src/shared/data/ast-grep-recipes.yaml +608 -0
  38. package/src/shared/data/ide-context-files.json +30 -0
  39. package/src/shared/health-check.md +31 -59
  40. package/src/shared/references/description-guard-protocol.md +12 -9
  41. package/src/shared/references/feasibility-report-schema.md +19 -13
  42. package/src/shared/references/headless-gate-convention.md +55 -3
  43. package/src/shared/references/output-contract-schema.md +19 -8
  44. package/src/shared/references/pipeline-contracts.md +94 -29
  45. package/src/shared/references/tessl-review.md +77 -0
  46. package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
  47. package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
  48. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
  49. package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
  50. package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
  51. package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
  52. package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
  53. package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
  54. package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
  55. package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
  56. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
  57. package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
  58. package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
  59. package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
  60. package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
  61. package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
  62. package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
  63. package/src/shared/scripts/skf-atomic-write.py +233 -64
  64. package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
  65. package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
  66. package/src/shared/scripts/skf-check-unit-records.py +184 -0
  67. package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
  68. package/src/shared/scripts/skf-classify-changed-files.py +772 -0
  69. package/src/shared/scripts/skf-comention-pairs.py +1333 -123
  70. package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
  71. package/src/shared/scripts/skf-count-imports.py +1867 -0
  72. package/src/shared/scripts/skf-count-tokens.py +1 -1
  73. package/src/shared/scripts/skf-derive-assembly-shape.py +21 -3
  74. package/src/shared/scripts/skf-description-guard.py +99 -11
  75. package/src/shared/scripts/skf-detect-docs.py +694 -2
  76. package/src/shared/scripts/skf-detect-language.py +486 -107
  77. package/src/shared/scripts/skf-detect-registry.py +887 -0
  78. package/src/shared/scripts/skf-detect-scripts-assets.py +197 -55
  79. package/src/shared/scripts/skf-detect-tools.py +209 -27
  80. package/src/shared/scripts/skf-detect-workspaces.py +506 -30
  81. package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
  82. package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
  83. package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
  84. package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
  85. package/src/shared/scripts/skf-extract-public-api.py +5247 -96
  86. package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
  87. package/src/shared/scripts/skf-extraction-snapshot.py +664 -0
  88. package/src/shared/scripts/skf-fetch-temporal.py +674 -0
  89. package/src/shared/scripts/skf-find-cycles.py +97 -29
  90. package/src/shared/scripts/skf-find-test-report.py +476 -0
  91. package/src/shared/scripts/skf-forge-tier-rw.py +469 -205
  92. package/src/shared/scripts/skf-github-fetch.py +365 -0
  93. package/src/shared/scripts/skf-github-probe.py +700 -0
  94. package/src/shared/scripts/skf-hash-content.py +180 -20
  95. package/src/shared/scripts/skf-language-corpora.py +1 -1
  96. package/src/shared/scripts/skf-load-provenance.py +161 -27
  97. package/src/shared/scripts/skf-manifest-ops.py +1 -1
  98. package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
  99. package/src/shared/scripts/skf-merge-doc-urls.py +143 -20
  100. package/src/shared/scripts/skf-names-present.py +221 -0
  101. package/src/shared/scripts/skf-pair-intersect.py +119 -28
  102. package/src/shared/scripts/skf-parse-gaps.py +1384 -0
  103. package/src/shared/scripts/skf-preapply.py +1 -1
  104. package/src/shared/scripts/skf-preflight.py +176 -28
  105. package/src/shared/scripts/skf-promote-staged.py +430 -0
  106. package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
  107. package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
  108. package/src/shared/scripts/skf-quick-batch.py +774 -0
  109. package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
  110. package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
  111. package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
  112. package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
  113. package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
  114. package/src/shared/scripts/skf-resolve-authoritative-files.py +155 -14
  115. package/src/shared/scripts/skf-resolve-package.py +523 -106
  116. package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
  117. package/src/shared/scripts/skf-run-lock.py +577 -0
  118. package/src/shared/scripts/skf-scan-manifests.py +998 -57
  119. package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
  120. package/src/shared/scripts/skf-severity-classify.py +712 -108
  121. package/src/shared/scripts/skf-shape-detect.py +298 -204
  122. package/src/shared/scripts/skf-shard-body.py +9 -8
  123. package/src/shared/scripts/skf-skill-inventory.py +2412 -89
  124. package/src/shared/scripts/skf-skills-module.py +472 -0
  125. package/src/shared/scripts/skf-source-tree.py +1796 -0
  126. package/src/shared/scripts/skf-structural-diff.py +787 -121
  127. package/src/shared/scripts/skf-tessl-review.py +706 -0
  128. package/src/shared/scripts/skf-update-active-symlink.py +120 -67
  129. package/src/shared/scripts/skf-update-run-state.py +624 -0
  130. package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
  131. package/src/shared/scripts/skf-validate-brief-schema.py +23 -2
  132. package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
  133. package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
  134. package/src/shared/scripts/skf-validate-output.py +712 -64
  135. package/src/shared/scripts/skf-validate-pins.py +1 -1
  136. package/src/shared/scripts/skf-verify-no-trace.py +116 -10
  137. package/src/shared/scripts/skf-verify-provenance-completeness.py +3945 -152
  138. package/src/shared/scripts/skf-write-skill-brief.py +406 -25
  139. package/src/shared/tool-requirements.yaml +201 -0
  140. package/src/skf-analyze-source/SKILL.md +41 -36
  141. package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
  142. package/src/skf-analyze-source/customize.toml +36 -24
  143. package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
  144. package/src/skf-analyze-source/references/continue.md +2 -13
  145. package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
  146. package/src/skf-analyze-source/references/generate-briefs.md +121 -55
  147. package/src/skf-analyze-source/references/headless-contract.md +42 -15
  148. package/src/skf-analyze-source/references/identify-units.md +96 -110
  149. package/src/skf-analyze-source/references/init.md +73 -71
  150. package/src/skf-analyze-source/references/map-and-detect.md +123 -105
  151. package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
  152. package/src/skf-analyze-source/references/recommend.md +51 -70
  153. package/src/skf-analyze-source/references/scan-project.md +60 -84
  154. package/src/skf-analyze-source/references/scan-root.md +39 -0
  155. package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
  156. package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
  157. package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
  158. package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
  159. package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
  160. package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
  161. package/src/skf-audit-skill/SKILL.md +43 -51
  162. package/src/skf-audit-skill/assets/drift-report-template.md +28 -3
  163. package/src/skf-audit-skill/customize.toml +41 -20
  164. package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
  165. package/src/skf-audit-skill/references/doc-drift.md +141 -0
  166. package/src/skf-audit-skill/references/headless-contract.md +29 -0
  167. package/src/skf-audit-skill/references/health-check.md +2 -0
  168. package/src/skf-audit-skill/references/init.md +149 -210
  169. package/src/skf-audit-skill/references/re-index.md +103 -110
  170. package/src/skf-audit-skill/references/report.md +96 -33
  171. package/src/skf-audit-skill/references/semantic-diff.md +34 -25
  172. package/src/skf-audit-skill/references/severity-classify.md +79 -65
  173. package/src/skf-audit-skill/references/severity-rules.md +15 -6
  174. package/src/skf-audit-skill/references/structural-diff.md +80 -90
  175. package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
  176. package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
  177. package/src/skf-brief-skill/SKILL.md +37 -26
  178. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  179. package/src/skf-brief-skill/assets/scope-templates.md +18 -25
  180. package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
  181. package/src/skf-brief-skill/customize.toml +48 -20
  182. package/src/skf-brief-skill/references/analyze-target.md +144 -127
  183. package/src/skf-brief-skill/references/confirm-brief.md +11 -9
  184. package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
  185. package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
  186. package/src/skf-brief-skill/references/gather-intent.md +87 -182
  187. package/src/skf-brief-skill/references/headless-args.md +84 -16
  188. package/src/skf-brief-skill/references/health-check.md +1 -1
  189. package/src/skf-brief-skill/references/invocation-contract.md +18 -13
  190. package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
  191. package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
  192. package/src/skf-brief-skill/references/scope-definition.md +123 -47
  193. package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
  194. package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
  195. package/src/skf-brief-skill/references/version-resolution.md +7 -10
  196. package/src/skf-brief-skill/references/write-brief.md +66 -57
  197. package/src/skf-campaign/SKILL.md +31 -34
  198. package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
  199. package/src/skf-campaign/customize.toml +47 -16
  200. package/src/skf-campaign/references/campaign-contracts.md +39 -29
  201. package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
  202. package/src/skf-campaign/references/health-check.md +7 -16
  203. package/src/skf-campaign/references/step-01-setup.md +46 -84
  204. package/src/skf-campaign/references/step-02-strategy.md +31 -30
  205. package/src/skf-campaign/references/step-03-pins.md +26 -18
  206. package/src/skf-campaign/references/step-04-provenance.md +24 -23
  207. package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
  208. package/src/skf-campaign/references/step-06-batch.md +41 -34
  209. package/src/skf-campaign/references/step-07-capstone.md +40 -25
  210. package/src/skf-campaign/references/step-08-verify.md +28 -29
  211. package/src/skf-campaign/references/step-09-refine.md +29 -30
  212. package/src/skf-campaign/references/step-10-export.md +48 -36
  213. package/src/skf-campaign/references/step-11-maintenance.md +31 -34
  214. package/src/skf-campaign/references/step-resume.md +37 -68
  215. package/src/skf-campaign/scripts/campaign-deps.py +57 -18
  216. package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
  217. package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
  218. package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
  219. package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
  220. package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
  221. package/src/skf-campaign/scripts/campaign-report.py +204 -37
  222. package/src/skf-campaign/scripts/campaign-state.py +1128 -0
  223. package/src/skf-campaign/scripts/campaign-status.py +10 -74
  224. package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
  225. package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
  226. package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
  227. package/src/skf-campaign/templates/campaign-report-template.md +5 -1
  228. package/src/skf-campaign/templates/kickoff-template.md +0 -14
  229. package/src/skf-create-skill/SKILL.md +27 -20
  230. package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
  231. package/src/skf-create-skill/assets/skill-sections.md +21 -11
  232. package/src/skf-create-skill/customize.toml +20 -8
  233. package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
  234. package/src/skf-create-skill/references/batch-mode.md +81 -0
  235. package/src/skf-create-skill/references/ccc-index-check.md +27 -0
  236. package/src/skf-create-skill/references/compile.md +119 -70
  237. package/src/skf-create-skill/references/component-extraction.md +113 -91
  238. package/src/skf-create-skill/references/enrich.md +41 -23
  239. package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
  240. package/src/skf-create-skill/references/extract.md +204 -162
  241. package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
  242. package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
  243. package/src/skf-create-skill/references/extraction-patterns.md +588 -201
  244. package/src/skf-create-skill/references/generate-artifacts.md +145 -93
  245. package/src/skf-create-skill/references/health-check.md +1 -1
  246. package/src/skf-create-skill/references/load-brief.md +53 -36
  247. package/src/skf-create-skill/references/relabel-rule.md +12 -0
  248. package/src/skf-create-skill/references/report.md +37 -57
  249. package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
  250. package/src/skf-create-skill/references/step-auto-shard.md +20 -93
  251. package/src/skf-create-skill/references/step-doc-rot.md +44 -60
  252. package/src/skf-create-skill/references/step-doc-sources.md +28 -19
  253. package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
  254. package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
  255. package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
  256. package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
  257. package/src/skf-create-skill/references/validate.md +173 -99
  258. package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
  259. package/src/skf-create-stack-skill/SKILL.md +29 -56
  260. package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
  261. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
  262. package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
  263. package/src/skf-create-stack-skill/customize.toml +23 -12
  264. package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
  265. package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
  266. package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
  267. package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
  268. package/src/skf-create-stack-skill/references/generate-output.md +138 -128
  269. package/src/skf-create-stack-skill/references/init.md +90 -32
  270. package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
  271. package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
  272. package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
  273. package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
  274. package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
  275. package/src/skf-create-stack-skill/references/report.md +39 -33
  276. package/src/skf-create-stack-skill/references/validate.md +20 -39
  277. package/src/skf-drop-skill/SKILL.md +28 -51
  278. package/src/skf-drop-skill/customize.toml +44 -30
  279. package/src/skf-drop-skill/references/execute.md +83 -146
  280. package/src/skf-drop-skill/references/health-check.md +2 -0
  281. package/src/skf-drop-skill/references/invocation-contract.md +64 -0
  282. package/src/skf-drop-skill/references/report.md +46 -30
  283. package/src/skf-drop-skill/references/select.md +122 -124
  284. package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
  285. package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
  286. package/src/skf-export-skill/SKILL.md +40 -62
  287. package/src/skf-export-skill/assets/managed-section-format.md +14 -50
  288. package/src/skf-export-skill/assets/snippet-format.md +3 -1
  289. package/src/skf-export-skill/customize.toml +42 -13
  290. package/src/skf-export-skill/references/generate-snippet.md +54 -46
  291. package/src/skf-export-skill/references/health-check.md +2 -0
  292. package/src/skf-export-skill/references/invocation-contract.md +15 -0
  293. package/src/skf-export-skill/references/load-skill.md +88 -62
  294. package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
  295. package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
  296. package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
  297. package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
  298. package/src/skf-export-skill/references/package.md +15 -73
  299. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
  300. package/src/skf-export-skill/references/result-envelope.md +31 -16
  301. package/src/skf-export-skill/references/summary.md +41 -17
  302. package/src/skf-export-skill/references/token-report.md +4 -12
  303. package/src/skf-export-skill/references/update-context.md +118 -237
  304. package/src/skf-forger/SKILL.md +61 -37
  305. package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
  306. package/src/skf-forger/customize.toml +43 -0
  307. package/src/skf-forger/references/pipeline-mode.md +73 -18
  308. package/src/skf-forger/scripts/forge-status.py +299 -0
  309. package/src/skf-forger/scripts/parse-pipeline.py +322 -54
  310. package/src/skf-forger/scripts/pipeline-gate.py +428 -0
  311. package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
  312. package/src/skf-quick-skill/SKILL.md +54 -53
  313. package/src/skf-quick-skill/assets/skill-template.md +4 -2
  314. package/src/skf-quick-skill/customize.toml +51 -20
  315. package/src/skf-quick-skill/references/batch-mode.md +74 -21
  316. package/src/skf-quick-skill/references/compile.md +30 -31
  317. package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
  318. package/src/skf-quick-skill/references/finalize.md +69 -18
  319. package/src/skf-quick-skill/references/halt-contract.md +41 -29
  320. package/src/skf-quick-skill/references/health-check.md +5 -3
  321. package/src/skf-quick-skill/references/quick-extract.md +114 -45
  322. package/src/skf-quick-skill/references/registry-resolution.md +9 -64
  323. package/src/skf-quick-skill/references/resolve-target.md +116 -66
  324. package/src/skf-quick-skill/references/write-and-validate.md +79 -40
  325. package/src/skf-refine-architecture/SKILL.md +43 -53
  326. package/src/skf-refine-architecture/customize.toml +49 -20
  327. package/src/skf-refine-architecture/references/compile.md +97 -54
  328. package/src/skf-refine-architecture/references/exit-codes.md +30 -0
  329. package/src/skf-refine-architecture/references/finding-storage.md +21 -0
  330. package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
  331. package/src/skf-refine-architecture/references/improvements.md +14 -20
  332. package/src/skf-refine-architecture/references/init.md +98 -57
  333. package/src/skf-refine-architecture/references/issue-detection.md +32 -39
  334. package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
  335. package/src/skf-refine-architecture/references/report.md +65 -54
  336. package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
  337. package/src/skf-rename-skill/SKILL.md +19 -54
  338. package/src/skf-rename-skill/customize.toml +32 -26
  339. package/src/skf-rename-skill/references/execute.md +230 -184
  340. package/src/skf-rename-skill/references/exit-codes.md +7 -7
  341. package/src/skf-rename-skill/references/health-check.md +1 -7
  342. package/src/skf-rename-skill/references/invocation-contract.md +25 -0
  343. package/src/skf-rename-skill/references/report.md +63 -52
  344. package/src/skf-rename-skill/references/select.md +197 -115
  345. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
  346. package/src/skf-setup/SKILL.md +26 -26
  347. package/src/skf-setup/customize.toml +14 -10
  348. package/src/skf-setup/references/auto-index.md +50 -63
  349. package/src/skf-setup/references/ccc-index.md +29 -90
  350. package/src/skf-setup/references/detect-and-tier.md +58 -67
  351. package/src/skf-setup/references/health-check.md +2 -0
  352. package/src/skf-setup/references/invocation-contract.md +14 -0
  353. package/src/skf-setup/references/report.md +43 -166
  354. package/src/skf-setup/references/tier-rules.md +5 -5
  355. package/src/skf-setup/references/write-config.md +34 -57
  356. package/src/skf-test-skill/SKILL.md +17 -57
  357. package/src/skf-test-skill/assets/output-section-formats.md +33 -34
  358. package/src/skf-test-skill/customize.toml +27 -13
  359. package/src/skf-test-skill/references/coherence-check.md +134 -62
  360. package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
  361. package/src/skf-test-skill/references/coverage-check.md +209 -264
  362. package/src/skf-test-skill/references/detect-mode.md +5 -5
  363. package/src/skf-test-skill/references/external-validators.md +94 -82
  364. package/src/skf-test-skill/references/health-check.md +6 -7
  365. package/src/skf-test-skill/references/init.md +164 -91
  366. package/src/skf-test-skill/references/invocation-contract.md +66 -0
  367. package/src/skf-test-skill/references/migration-section-rules.md +5 -2
  368. package/src/skf-test-skill/references/report.md +164 -142
  369. package/src/skf-test-skill/references/score.md +80 -107
  370. package/src/skf-test-skill/references/scoring-rules.md +42 -155
  371. package/src/skf-test-skill/references/source-access-protocol.md +18 -94
  372. package/src/skf-test-skill/references/step-hard-gate.md +51 -33
  373. package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
  374. package/src/skf-test-skill/scripts/build-result-context.py +380 -0
  375. package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
  376. package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
  377. package/src/skf-test-skill/scripts/compute-score.py +223 -45
  378. package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
  379. package/src/skf-test-skill/scripts/hard-gate.py +203 -0
  380. package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
  381. package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
  382. package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
  383. package/src/skf-test-skill/scripts/score-signatures.py +479 -0
  384. package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
  385. package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
  386. package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
  387. package/src/skf-test-skill/templates/test-report-template.md +21 -15
  388. package/src/skf-update-skill/SKILL.md +38 -25
  389. package/src/skf-update-skill/customize.toml +27 -12
  390. package/src/skf-update-skill/references/detect-changes.md +253 -223
  391. package/src/skf-update-skill/references/gap-driven.md +336 -0
  392. package/src/skf-update-skill/references/health-check.md +31 -5
  393. package/src/skf-update-skill/references/init.md +254 -84
  394. package/src/skf-update-skill/references/invocation-contract.md +12 -0
  395. package/src/skf-update-skill/references/manual-section-rules.md +6 -23
  396. package/src/skf-update-skill/references/merge.md +182 -49
  397. package/src/skf-update-skill/references/re-extract.md +53 -262
  398. package/src/skf-update-skill/references/report.md +102 -35
  399. package/src/skf-update-skill/references/write.md +357 -155
  400. package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
  401. package/src/skf-verify-stack/SKILL.md +23 -42
  402. package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
  403. package/src/skf-verify-stack/customize.toml +55 -22
  404. package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
  405. package/src/skf-verify-stack/references/coverage.md +67 -64
  406. package/src/skf-verify-stack/references/exit-codes.md +21 -8
  407. package/src/skf-verify-stack/references/init.md +107 -62
  408. package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
  409. package/src/skf-verify-stack/references/integrations.md +106 -65
  410. package/src/skf-verify-stack/references/report.md +59 -42
  411. package/src/skf-verify-stack/references/requirements.md +32 -9
  412. package/src/skf-verify-stack/references/synthesize.md +53 -39
  413. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
  414. package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
  415. package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
  416. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
  417. package/tools/cli/commands/install.js +11 -4
  418. package/tools/cli/commands/status.js +15 -3
  419. package/tools/cli/commands/update.js +11 -4
  420. package/tools/cli/lib/tool-check.js +320 -0
  421. package/tools/cli/lib/version-check.js +22 -9
  422. package/tools/cli/skf-cli.js +4 -1
  423. package/.gitattributes +0 -16
  424. package/.nvmrc +0 -1
  425. package/docs/_internal/RELEASING.md +0 -679
  426. package/release-audits/v1.0.0-launch-audit.md +0 -728
  427. package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
  428. package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
  429. package/src/skf-campaign/manifest.yaml +0 -15
  430. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
  431. package/src/skf-create-skill/references/ecosystem-check.md +0 -74
  432. package/src/skf-drop-skill/references/headless-contract.md +0 -18
  433. package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
  434. package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
  435. package/src/skf-update-skill/references/validate.md +0 -124
  436. package/tools/validate-docs-drift.js +0 -233
  437. package/tools/validate-docs-links.js +0 -665
@@ -1,46 +1,80 @@
1
1
  #!/usr/bin/env python3
2
2
  # /// script
3
- # requires-python = ">=3.10"
3
+ # requires-python = ">=3.11"
4
+ # dependencies = ["pyyaml"]
4
5
  # ///
5
6
  """Deterministic NEW_FILE detection for skf-update-skill (detect-changes Category D).
6
7
 
7
8
  `skf-hash-content compare` classifies files already tracked in the provenance
8
9
  map (UNCHANGED / MODIFIED_FILE / DELETED_FILE) but by design cannot report a
9
- file that is present in source yet absent from the provenance map — a NEW_FILE.
10
+ file that is present in source yet absent from the provenance map: a NEW_FILE.
10
11
  Deriving that set is a set-difference plus a filter with exactly one correct
11
12
  answer per input: take every `source_file` in the scripts/assets inventory
12
13
  (emitted by `skf-detect-scripts-assets detect`), subtract the paths already in
13
- `file_entries[].source_file`, and set aside any user-authored `[MANUAL]` path.
14
- Doing that subtraction in the prompt drifts across runs; doing it here is
15
- byte-stable.
14
+ `file_entries[].source_file`, and set aside any user-authored `[MANUAL]` path,
15
+ any path the map's `entries[]` cite (code Category A and B track) and, with
16
+ --brief, any path the skill brief leaves out. Doing that subtraction in the
17
+ prompt drifts across runs; doing it here is byte-stable.
16
18
 
17
19
  Usage:
18
- skf-detect-scripts-assets.py detect <source-root> \
19
- | skf-new-file-diff.py <provenance-map-path>
20
+ skf-detect-scripts-assets.py detect <source-root> \\
21
+ | skf-new-file-diff.py <provenance-map-path> [--brief <brief-path>]
20
22
 
21
23
  Reads the detect JSON on stdin (needs `scripts_inventory[]` and
22
24
  `assets_inventory[]`, each row carrying `source_file`). Reads the provenance
23
- map at the path argument (canonical `{file_entries: [...]}` object, or a bare
24
- array of entries).
25
+ map at the path argument (canonical `{file_entries: [...], entries: [...]}`
26
+ object, or a bare array of file entries). A map with no `file_entries`, or a
27
+ null one, tracks no file (a skill with no script, asset or promoted document,
28
+ or a map written before the field existed); a `file_entries` or `entries`
29
+ that is neither null nor an array is bad input.
30
+
31
+ --brief <brief-path> (the skill's skill-brief.yaml) keeps a detected path
32
+ only when the brief takes it, by update-skill Category A's in-scope test
33
+ (skf-classify-changed-files.py load_scope, loaded from the shared
34
+ scripts folder, the one brief-scope test the script and asset detector's
35
+ and audit-skill's --brief use too, with the glob rules of
36
+ skf-resolve-authoritative-files.py): each `scope.include` and
37
+ `scope.exclude` pattern normalized (forward slashes, no leading `./`),
38
+ and a path in scope when an include
39
+ matches it and no exclude does. With an empty `scope.include`, a path is in
40
+ scope when no exclude matches it and its extension is one a file the map's
41
+ `entries[]` cite has, or one the brief's `language` uses. The brief's
42
+ `scripts_intent` and `assets_intent` are honoured too: `none` drops every
43
+ detected path of that kind, and an absent or free-text intent detects.
44
+ An empty --brief (an unbound brief path) is a usage error, never an
45
+ unscoped run. Every path, the map's and the detector's, is compared
46
+ stripped, with forward slashes and no leading `./`.
25
47
 
26
48
  Output JSON (stdout):
27
49
  {
28
50
  "new_files": [ {"source_file": "...", "kind": "script|asset"}, ... ],
29
51
  "skipped_manual": [ "...", ... ], # under scripts/[MANUAL]/ or assets/[MANUAL]/
30
52
  "already_tracked": [ "...", ... ], # present in file_entries
53
+ "tracked_code": [ "...", ... ], # cited by entries[]: code, not a new file
54
+ "out_of_scope": [ "...", ... ], # --brief: outside the brief's scope
55
+ "intent_none": [ "...", ... ], # --brief: a kind whose intent is none
31
56
  "stats": {"inventory_total": N, "new": N,
32
57
  "skipped_manual": N, "already_tracked": N}
33
58
  }
34
59
 
35
- All three arrays are sorted by source_file; new_files carries the kind of the
36
- inventory it came from (scripts -> "script", assets -> "asset"). A path found
37
- in both inventories is counted once, resolved as "script".
60
+ Each detected path lands in exactly one list, tested in this order:
61
+ already_tracked, skipped_manual, tracked_code, out_of_scope, intent_none,
62
+ and new_files for the rest. All arrays are sorted by
63
+ source_file; new_files carries the kind of the inventory it came from
64
+ (scripts -> "script", assets -> "asset"). A path found in both inventories is
65
+ counted once, resolved as "script". The three set-aside lists (tracked_code,
66
+ out_of_scope, intent_none) are never counted in `stats`, whose
67
+ inventory_total still counts every detected path.
38
68
 
39
69
  Exit codes:
40
- 0 success
41
- 2 bad input (unreadable/invalid stdin JSON, missing/invalid provenance map)
70
+ 0 success (and --help)
71
+ 2 bad input (a usage error, unreadable/invalid stdin JSON, missing/invalid
72
+ provenance map, a missing or unreadable brief, or a shared helper that
73
+ cannot be loaded)
42
74
  """
43
75
 
76
+ import argparse
77
+ import importlib.util
44
78
  import json
45
79
  import re
46
80
  import sys
@@ -49,6 +83,13 @@ from pathlib import Path
49
83
  # scripts/[MANUAL]/... or assets/[MANUAL]/... anywhere in the (posix) path
50
84
  _MANUAL_RE = re.compile(r"(?:^|/)(?:scripts|assets)/\[MANUAL\]/")
51
85
 
86
+ # The shared helper that holds the scope test, installed beside this skill's
87
+ # folder: it loads skf-resolve-authoritative-files.py beside it.
88
+ SHARED_SCRIPTS = Path(__file__).resolve().parent.parent.parent / "shared" / "scripts"
89
+ CLASSIFIER = "skf-classify-changed-files.py"
90
+
91
+ _SHARED: dict[str, object] = {}
92
+
52
93
 
53
94
  def _fail(msg: str) -> "NoReturn": # type: ignore[valid-type]
54
95
  print(json.dumps({"error": msg}), file=sys.stderr)
@@ -56,37 +97,88 @@ def _fail(msg: str) -> "NoReturn": # type: ignore[valid-type]
56
97
 
57
98
 
58
99
  def _posix(p: str) -> str:
59
- return p.replace("\\", "/")
100
+ """A path as skf-resolve-authoritative-files.py's normalize_rel_path
101
+ writes it: stripped, forward slashes, no leading `./`."""
102
+ p = p.strip().replace("\\", "/")
103
+ while p.startswith("./"):
104
+ p = p[2:]
105
+ return p
60
106
 
61
107
 
62
- def load_tracked_source_files(provenance_path: Path) -> set[str]:
63
- """Return the set of file_entries[].source_file already in the provenance map."""
108
+ def _shared(filename: str):
109
+ """A script of SHARED_SCRIPTS, loaded once; a failure to load it is bad input (exit 2)."""
110
+ module = _SHARED.get(filename)
111
+ if module is None:
112
+ path = SHARED_SCRIPTS / filename
113
+ try:
114
+ spec = importlib.util.spec_from_file_location("skf_" + filename[:-3].replace("-", "_"), path)
115
+ if spec is None or spec.loader is None or not path.is_file():
116
+ raise ImportError(f"{filename} not found in {SHARED_SCRIPTS}")
117
+ module = importlib.util.module_from_spec(spec)
118
+ spec.loader.exec_module(module)
119
+ except Exception as exc: # whatever the script raises, the run reports it
120
+ _fail(f"cannot load {filename} from {SHARED_SCRIPTS}: {exc}; re-install SKF")
121
+ _SHARED[filename] = module
122
+ return module
123
+
124
+
125
+ def _source_files(rows: list) -> set[str]:
126
+ found: set[str] = set()
127
+ for entry in rows:
128
+ if isinstance(entry, dict):
129
+ sf = entry.get("source_file")
130
+ if isinstance(sf, str) and _posix(sf):
131
+ found.add(_posix(sf))
132
+ return found
133
+
134
+
135
+ def load_map_paths(provenance_path: Path) -> tuple[set[str], set[str]]:
136
+ """(file_entries[].source_file, entries[].source_file) of the provenance map.
137
+
138
+ A missing or null `file_entries` (or `entries`) is an empty list; one
139
+ that is neither null nor an array is bad input. A bare array is the
140
+ file entries themselves.
141
+ """
64
142
  try:
65
143
  data = json.loads(provenance_path.read_text(encoding="utf-8"))
66
144
  except (json.JSONDecodeError, OSError) as exc:
67
145
  _fail(f"failed to read provenance map {provenance_path}: {exc}")
68
146
 
69
147
  if isinstance(data, list):
70
- entries = data
71
- elif isinstance(data, dict):
72
- entries = data.get("file_entries")
73
- if entries is None:
74
- _fail(f"provenance map {provenance_path} has no `file_entries` field")
75
- if not isinstance(entries, list):
76
- _fail(f"`file_entries` in {provenance_path} is not an array")
77
- else:
148
+ return _source_files(data), set()
149
+ if not isinstance(data, dict):
78
150
  _fail(
79
151
  f"provenance map {provenance_path} must be an object or array; "
80
152
  f"got {type(data).__name__}"
81
153
  )
154
+ rows = {}
155
+ for key in ("file_entries", "entries"):
156
+ value = data.get(key)
157
+ if value is None:
158
+ value = [] # a map that tracks no such row
159
+ if not isinstance(value, list):
160
+ _fail(f"`{key}` in {provenance_path} is not an array")
161
+ rows[key] = value
162
+ return _source_files(rows["file_entries"]), _source_files(rows["entries"])
82
163
 
83
- tracked: set[str] = set()
84
- for entry in entries:
85
- if isinstance(entry, dict):
86
- sf = entry.get("source_file")
87
- if isinstance(sf, str) and sf:
88
- tracked.add(_posix(sf))
89
- return tracked
164
+
165
+ def load_tracked_source_files(provenance_path: Path) -> set[str]:
166
+ """Return the set of file_entries[].source_file already in the provenance map."""
167
+ return load_map_paths(provenance_path)[0]
168
+
169
+
170
+ def load_scope(brief_path: Path, cited: set[str]):
171
+ """The brief's Scope (skf-classify-changed-files.py load_scope): its
172
+ takes(path) is Category A's in-scope test and its wants(kind) the
173
+ brief's intent. A brief it cannot find or read, or a shared helper it
174
+ cannot load, is bad input (exit 2)."""
175
+ classifier = _shared(CLASSIFIER)
176
+ try:
177
+ return classifier.load_scope(brief_path, cited)
178
+ except ValueError as exc: # a brief it cannot find or read
179
+ _fail(str(exc))
180
+ except Exception as exc: # an out-of-date classifier, or a resolver beside it that cannot load
181
+ _fail(f"cannot load the brief's scope test from {SHARED_SCRIPTS}: {exc}; re-install SKF")
90
182
 
91
183
 
92
184
  def collect_inventory(detect: dict) -> list[tuple[str, str]]:
@@ -112,38 +204,82 @@ def collect_inventory(detect: dict) -> list[tuple[str, str]]:
112
204
  return sorted(seen.items())
113
205
 
114
206
 
115
- def diff(detect: dict, tracked: set[str]) -> dict:
207
+ def diff(detect: dict, tracked: set[str], cited: set[str] | frozenset = frozenset(),
208
+ scope=None) -> dict:
209
+ """Sort the detected paths into the output lists; `scope` is load_scope's
210
+ Scope (--brief) or None."""
116
211
  new_files: list[dict] = []
117
- skipped_manual: list[str] = []
118
- already_tracked: list[str] = []
212
+ lists: dict[str, list[str]] = {name: [] for name in (
213
+ "skipped_manual", "already_tracked", "tracked_code", "out_of_scope", "intent_none")}
119
214
 
120
215
  inventory = collect_inventory(detect)
121
216
  for sf, kind in inventory:
122
217
  if sf in tracked:
123
- already_tracked.append(sf)
218
+ lists["already_tracked"].append(sf)
124
219
  elif _MANUAL_RE.search(sf):
125
- skipped_manual.append(sf)
220
+ lists["skipped_manual"].append(sf)
221
+ elif sf in cited:
222
+ lists["tracked_code"].append(sf)
223
+ elif scope is not None and not scope.takes(sf):
224
+ lists["out_of_scope"].append(sf)
225
+ elif scope is not None and not scope.wants(kind):
226
+ lists["intent_none"].append(sf)
126
227
  else:
127
228
  new_files.append({"source_file": sf, "kind": kind})
128
229
 
129
230
  return {
130
231
  "new_files": new_files,
131
- "skipped_manual": sorted(skipped_manual),
132
- "already_tracked": sorted(already_tracked),
232
+ "skipped_manual": sorted(lists["skipped_manual"]),
233
+ "already_tracked": sorted(lists["already_tracked"]),
234
+ "tracked_code": sorted(lists["tracked_code"]),
235
+ "out_of_scope": sorted(lists["out_of_scope"]),
236
+ "intent_none": sorted(lists["intent_none"]),
133
237
  "stats": {
134
238
  "inventory_total": len(inventory),
135
239
  "new": len(new_files),
136
- "skipped_manual": len(skipped_manual),
137
- "already_tracked": len(already_tracked),
240
+ "skipped_manual": len(lists["skipped_manual"]),
241
+ "already_tracked": len(lists["already_tracked"]),
138
242
  },
139
243
  }
140
244
 
141
245
 
246
+ USAGE = "usage: skf-new-file-diff.py <provenance-map-path> [--brief <brief-path>] (detect JSON on stdin)"
247
+
248
+
249
+ class _JsonErrorParser(argparse.ArgumentParser):
250
+ """Report a usage error as the JSON error and exit 2 every other bad input gets."""
251
+
252
+ def error(self, message: str) -> "NoReturn": # type: ignore[valid-type]
253
+ _fail(f"{USAGE}: {message}")
254
+
255
+
256
+ def _build_parser() -> argparse.ArgumentParser:
257
+ parser = _JsonErrorParser(
258
+ prog="skf-new-file-diff.py",
259
+ description=__doc__,
260
+ formatter_class=argparse.RawDescriptionHelpFormatter,
261
+ allow_abbrev=False,
262
+ )
263
+ parser.add_argument(
264
+ "provenance_map",
265
+ metavar="provenance-map-path",
266
+ help="the skill's provenance-map.json (the detect JSON is read on stdin)",
267
+ )
268
+ parser.add_argument(
269
+ "--brief",
270
+ metavar="brief-path",
271
+ help="the skill brief: keep only the paths its scope takes and its intents want",
272
+ )
273
+ return parser
274
+
275
+
142
276
  def main(argv: list[str]) -> int:
143
- if len(argv) != 2:
144
- _fail("usage: skf-new-file-diff.py <provenance-map-path> (detect JSON on stdin)")
277
+ parser = _build_parser()
278
+ args = parser.parse_args(argv[1:])
279
+ if args.brief is not None and not args.brief.strip():
280
+ parser.error("--brief is empty (an unbound brief path?): pass the brief, or leave --brief out")
145
281
 
146
- provenance_path = Path(argv[1])
282
+ provenance_path = Path(args.provenance_map)
147
283
  if not provenance_path.exists():
148
284
  _fail(f"provenance map not found: {provenance_path}")
149
285
 
@@ -157,8 +293,9 @@ def main(argv: list[str]) -> int:
157
293
  if not isinstance(detect, dict):
158
294
  _fail(f"detect JSON must be an object; got {type(detect).__name__}")
159
295
 
160
- tracked = load_tracked_source_files(provenance_path)
161
- result = diff(detect, tracked)
296
+ tracked, cited = load_map_paths(provenance_path)
297
+ scope = load_scope(Path(args.brief), cited) if args.brief is not None else None
298
+ result = diff(detect, tracked, cited, scope)
162
299
  print(json.dumps(result, indent=2))
163
300
  return 0
164
301
 
@@ -7,7 +7,7 @@ description: Pre-code stack feasibility verification against architecture and PR
7
7
 
8
8
  ## Overview
9
9
 
10
- Cross-references generated skills against architecture and PRD documents to produce a feasibility report with evidence-backed integration verdicts, coverage analysis, and requirements mapping. Read-only: it reads skills and input documents and writes only the feasibility report (see Workflow Rules).
10
+ Cross-references generated skills against architecture and PRD documents to produce a feasibility report with evidence-backed integration verdicts, coverage analysis, and requirements mapping. Read-only: it reads skills and input documents and writes only the feasibility report, its result files and a scratch run folder (see Workflow Rules).
11
11
 
12
12
  **Schema contract:** This skill is the producer of the SKF shared feasibility report schema — every report conforms to it.
13
13
 
@@ -18,6 +18,8 @@ Cross-references generated skills against architecture and PRD documents to prod
18
18
  - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).
19
19
  - `{project-root}`-prefixed paths resolve from the project working directory.
20
20
  - `{skill-name}` resolves to the skill directory's basename.
21
+ - **Module-level path exception:** bare paths beginning with `knowledge/` or `shared/` resolve from the SKF module root (`{project-root}/_bmad/skf/` installed, `src/` in dev), not the skill root; e.g. `shared/references/output-contract-schema.md` and `shared/health-check.md`.
22
+ - **Sibling skills:** a path that names another SKF skill's folder (`skf-<name>/...`) resolves from the SKF module root, and that skill must be installed with this one.
21
23
 
22
24
  ## Role
23
25
 
@@ -33,7 +35,11 @@ These rules apply to every step in this workflow:
33
35
  - If any instruction references a subprocess or tool you lack, achieve the outcome in your main context thread
34
36
  - Always communicate in `{communication_language}`
35
37
  - At any interactive prompt, the inputs `cancel`, `exit`, `[X]`, `q`, or `:q` exit cleanly with exit code 6 (`halt_reason: "user-cancelled"`)
36
- - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
38
+ - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision; a gate that picks its default for the user (step 1's offer of the newest earlier report) also records that decision in the run sink the moment it decides
39
+ - Every helper a stage runs (a `scripts/` file, or a shared helper from the stage's probe order) is required: if the probe order resolves to no path, or the helper cannot start, HALT (exit code 3, `halt_reason: "resolution-failure"`) at the stage's phase, through the stage's halt envelope. Never recompute a helper's result by hand: the main-thread rule above covers subagent work, not helpers.
40
+ - Every HARD HALT names its exit code, `halt_reason` and phase, and in headless mode prints its envelope through the shared emitter, with the command each stage file shows (`references/exit-codes.md` describes the envelope).
41
+ - Stages 1 to 5 write only the timestamped report `{outputFile}`, and each write that fails halts (exit code 4, `halt_reason: "write-failed"`). Step 6 copies the report to `{outputFileLatest}` only after it passes the feasibility-report check, so a run that halts before then leaves the previous finished copy, the one consumers read, in place.
42
+ - Run state lives in `{run_dir}`, not in context; step 6 deletes it, a HALT keeps it.
37
43
 
38
44
  ## Stages
39
45
 
@@ -44,7 +50,7 @@ These rules apply to every step in this workflow:
44
50
  | 3 | Integration Verification | references/integrations.md | Yes |
45
51
  | 4 | Requirements Mapping | references/requirements.md | Yes |
46
52
  | 5 | Synthesize Verdict | references/synthesize.md | Yes |
47
- | 6 | Report | references/report.md | No (confirm) |
53
+ | 6 | Report | references/report.md | Yes |
48
54
  | 7 | Workflow Health Check | references/health-check.md | Yes |
49
55
 
50
56
  ## Invocation Contract
@@ -53,20 +59,14 @@ These rules apply to every step in this workflow:
53
59
  |--------|--------|
54
60
  | **Inputs** | architecture_doc_path [required], prd_path [optional], previous_report_path [optional] |
55
61
  | **Flags** | `--headless` / `-H` (auto-resolve all gates); `--architecture-doc <path>` (skip step 1 prompt for the required input); `--prd <path>` (skip step 1 prompt for the optional PRD); `--previous-report <path>` (skip step 1 prompt for delta comparison) |
56
- | **Gates** | step 1 Input Gate (use args); step 6 Report Menu ([R] review / [X] exit, headless default X). Steps 2-3 also hold elective vacuous-analysis guards (0% coverage, all-Blocked) that only fire in degenerate cases; every guard auto-resolves to Continue in headless. |
57
- | **Outputs** | `feasibility-report-{projectSlug}-{timestamp}.md` and `feasibility-report-{projectSlug}-latest.md` (copy, not symlink) per the SKF shared feasibility report schema (`_bmad/skf/shared/references/feasibility-report-schema.md`; `src/shared/references/…` in a dev checkout) — with integration verdicts, coverage analysis, recommendations, and evidence sources; plus `verify-stack-result-{timestamp}.json` and `verify-stack-result-latest.json` |
62
+ | **Gates** | step 1 Input Gate (use args), including the offer to compare against the newest earlier report (headless default: use it). No later stage waits for input: a run with 0% coverage or only Blocked pairs prints one warning and continues, so its report still carries the recommendations. |
63
+ | **Outputs** | `feasibility-report-{project_slug}-{timestamp}.md` and its `feasibility-report-{project_slug}-latest.md` copy (not a symlink), written by step 6 once the finished report passes the feasibility-report check, both per the SKF shared feasibility report schema (`{project-root}/_bmad/skf/shared/references/feasibility-report-schema.md`; `{project-root}/src/shared/references/feasibility-report-schema.md` in a dev checkout), with integration verdicts, coverage analysis, recommendations, and evidence sources; plus `verify-stack-result-{YYYYMMDD-HHmmss}.json` and `verify-stack-result-latest.json` in `{forge_data_folder}` |
58
64
  | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true. Per-flag args (`--architecture-doc`, `--prd`, `--previous-report`) consumed at the gates that would otherwise prompt. |
59
65
  | **Exit codes** | See `references/exit-codes.md` |
60
66
 
61
67
  ## Result Contract (Headless)
62
68
 
63
- When `{headless_mode}` is true, step 6 emits a single-line JSON envelope on **stdout** before chaining to step 7, and every headless hard halt emits the same envelope shape on **stderr** with `status: "error"`:
64
-
65
- ```
66
- SKF_VERIFY_STACK_RESULT_JSON: {"status":"success|error","report_path":"…|null","report_latest_path":"…|null","overall_verdict":"…|null","coverage_percentage":0,"recommendation_count":0,"exit_code":0,"halt_reason":null}
67
- ```
68
-
69
- `status` is `"success"` on the terminal happy path, `"error"` on any halt. `halt_reason` is one of: `null` (success), `"input-missing"`, `"input-invalid"`, `"skills-folder-missing"`, `"insufficient-skills"`, `"forge-folder-unconfigured"`, `"resolution-failure"`, `"previous-report-collision"`, `"inventory-unreliable"`, `"schema-violation"`, `"write-failed"`, `"user-cancelled"`. `exit_code` matches `references/exit-codes.md` (the `analysis-halted` / exit-8 gates are interactive-only, so they never reach this envelope). `overall_verdict` uses the schema tokens (`FEASIBLE`/`CONDITIONALLY_FEASIBLE`/`NOT_FEASIBLE`).
69
+ When `{headless_mode}` is true, step 6 prints one `SKF_VERIFY_STACK_RESULT_JSON: {...}` line on **stdout** and every HARD HALT one on **stderr**, built by the shared emitter: `references/exit-codes.md` gives its fields, the `halt_reason` values and the halt command.
70
70
 
71
71
  ## On Activation
72
72
 
@@ -74,49 +74,30 @@ SKF_VERIFY_STACK_RESULT_JSON: {"status":"success|error","report_path":"…|null"
74
74
  - `project_name`, `user_name`, `communication_language`, `document_output_language`
75
75
  - `skills_output_folder`, `forge_data_folder`, `sidecar_path`
76
76
 
77
- 2. **Compute run-scoped variables** (same place as config so every stage can reference them without re-derivation):
78
- - `project_slug` ← slugify `project_name` (lowercase, hyphens only, no unicode, no whitespace)
77
+ 2. **Fix the run-scoped variables** so no stage derives them again:
79
78
  - `timestamp` ← UTC `YYYYMMDD-HHmmss` captured at activation time
80
- - These two combine in init.md §4 into `{outputFile}` per the stage frontmatter template, but the values themselves are fixed for the entire workflow run — every later reference to `{outputFile}` resolves consistently.
79
+ - `project_slug` is bound by init.md before its §1, from the shared feasibility-report helper, which holds the one slug rule the consumers of this report also apply; never slugify `project_name` by hand
80
+ - The two combine in init.md §4 into `{outputFile}` and `{outputFileLatest}` per the stage frontmatter template, and stay fixed for the whole run, so every later reference to either file resolves to the same path.
81
+ - `run_dir` ← `{project-root}/_bmad-output/.skf-run/skf-verify-stack-{timestamp}`, the run folder init.md creates first
81
82
 
82
83
  3. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `{sidecar_path}/preferences.yaml`. Default: false.
83
84
 
85
+ **Bind the inputs** in every mode: `{architecture_doc_path}` ← the `--architecture-doc` value, `{prd_path}` ← `--prd` and `{previous_report_path}` ← `--previous-report`, each null when its flag is absent. init.md §1 asks only for those still null.
86
+
84
87
  4. **Resolve workflow customization.** Run:
85
88
 
86
89
  ```bash
87
- python3 {project-root}/_bmad/scripts/resolve_customization.py \
88
- --skill {skill-root} --key workflow
90
+ uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --project-root {project-root} --key workflow
89
91
  ```
90
92
 
91
- The script merges the three customization layers per `bmad-customize`'s structural merge rules (scalars override, arrays append):
92
-
93
- - `{skill-root}/customize.toml` — bundled defaults
94
- - `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
95
- - `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
96
-
97
- If the script fails or is missing, fall back to reading `{skill-root}/customize.toml` directly — the bundled defaults are an empty string for each path scalar.
98
-
99
- Apply the path-scalar fallback now so stage files don't have to repeat the conditional logic. For each of the four scalars, if the merged value is empty or absent, use the bundled default:
93
+ It merges the bundled `{skill-root}/customize.toml` with `{project-root}/_bmad/custom/skf-verify-stack.toml` (team overrides, committed) and `.user.toml` (personal overrides, gitignored). When it exits non-zero, prints no JSON or is missing, print one line, `[activation/warn] customization_resolver_unavailable: <reason>` (`<reason>`: its first stderr line, `not found` when the script is missing, `no JSON` when it printed none). If the resolver cannot run, read `{skill-root}/customize.toml` alone and use its bundled defaults: the `{project-root}/_bmad/custom/` overrides do not apply to this run. In that case keep the reason as `{customization_resolver_unavailable}` (unset when the resolver ran) for init.md to record once it has created `{run_dir}`.
100
94
 
101
- - `{reportTemplatePath}` ← `workflow.report_template_path` if non-empty, else `assets/feasibility-report-template.md`
102
- - `{integrationRulesPath}` ← `workflow.integration_rules_path` if non-empty, else `references/integration-verification-rules.md`
103
- - `{coveragePatternsPath}` ← `workflow.coverage_patterns_path` if non-empty, else `references/coverage-patterns.md`
104
- - `{outputFolderPath}` ← `workflow.output_folder_path` if non-empty, else `{forge_data_folder}`
105
-
106
- Stash all four as workflow-context variables. Stage files reference them directly — no conditional at the usage site. Empty-string overrides cleanly fall through to the bundled default.
95
+ Apply the path-scalar fallback now so stage files don't repeat it: `{reportTemplatePath}` ← `workflow.report_template_path` if non-empty, else `assets/feasibility-report-template.md`. An empty or absent value falls through to that default, and init.md §4 loads the variable as it is. Bind `{outputFolderPath}` ← `{forge_data_folder}`, always (no setting moves it): the report and its `-latest` copy go where create-stack-skill and refine-architecture look for them, and step 1 finds the earlier reports there.
107
96
 
108
97
  The same merge resolves `workflow.on_complete` (default empty = no-op); report.md §5 executes it, if non-empty, at the terminal stage.
109
98
 
110
- Also apply the array surfaces so they are not silent no-ops: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (`file:`-prefixed entries load their file/glob contents as facts — the bundled default glob is `{project-root}/**/project-context.md`); then execute each entry in `workflow.activation_steps_append` after activation completes.
111
-
112
- 5. **Pre-flight write probe.** Verify `{outputFolderPath}` is writable. A read-only mount, full disk, or permissions-denied path otherwise only surfaces at init.md §4 atomic write — by then the user has already gone through the input prompts:
113
-
114
- ```bash
115
- mkdir -p "{outputFolderPath}" && \
116
- printf 'probe' > "{outputFolderPath}/.skf-write-probe" && \
117
- rm "{outputFolderPath}/.skf-write-probe"
118
- ```
99
+ Also apply the array surfaces so they are not silent no-ops: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (`file:`-prefixed entries load their file/glob contents as facts: the bundled default loads every `project-context.md` under `{project-root}`; an entry prefixed `!` drops each earlier entry it names and loads nothing itself, so an override's `"!file:{project-root}/**/project-context.md"` turns that default off); then execute each entry in `workflow.activation_steps_append` after activation completes.
119
100
 
120
- On any non-zero exit: HALT (exit code 4, `halt_reason: "write-failed"`). In headless mode, emit the error envelope per **Result Contract (Headless)** with `report_path: null`, `report_latest_path: null`, `overall_verdict: null`.
101
+ 5. **Pre-flight: the emitter.** Before the first prompt, resolve `{emitEnvelopeHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py`, else `{project-root}/src/shared/scripts/skf-emit-result-envelope.py` (the first that exists). If neither exists, HALT (exit code 3, `halt_reason: "resolution-failure"`) at phase `on-activation:emitter` and display only: "Verify Stack cannot run without `skf-emit-result-envelope.py`, which is not installed. Re-install SKF."
121
102
 
122
103
  6. Load, read the full file, and then execute `references/init.md` to begin the workflow.
@@ -19,6 +19,9 @@ architectureDoc: ''
19
19
  prdDoc: ''
20
20
  previousReport: ''
21
21
  skillsAnalyzed: 0
22
+ coverageCovered: null
23
+ coverageMissing: null
24
+ coverageReplaced: null
22
25
  stepsCompleted: []
23
26
  requirementsPass: ''
24
27
  requirementsFulfilled: null
@@ -55,9 +58,8 @@ deltaUnchanged: null
55
58
 
56
59
  ## Integration Verdicts
57
60
 
58
- <!-- Appended by integrations.
59
- Consumers grep for the `## Integration Verdicts` heading to locate the pair table.
60
- The table header is fixed and MUST be emitted exactly as shown below: -->
61
+ <!-- Filled in place by integrations: one row per pair goes under this header.
62
+ The report holds this table once: consumers read it and reject a second copy. -->
61
63
 
62
64
  | lib_a | lib_b | verdict | rationale |
63
65
  |-------|-------|---------|-----------|
@@ -72,5 +74,9 @@ The table header is fixed and MUST be emitted exactly as shown below: -->
72
74
 
73
75
  ## Evidence Sources
74
76
 
75
- <!-- Appended by synthesize — cite each skill's SKILL.md path, metadata_schema_version,
76
- confidence_tier, stack manifest (if any), and architecture/PRD doc paths -->
77
+ <!-- Filled in place by synthesize: one row per skill goes under this header (the
78
+ next run's delta reads its evidence_tier column), then the architecture and PRD
79
+ document paths. -->
80
+
81
+ | skill | evidence_tier | confidence_tier | metadata_schema_version | skill_md |
82
+ |-------|---------------|-----------------|-------------------------|----------|
@@ -1,8 +1,8 @@
1
1
  # DO NOT EDIT -- overwritten on every update.
2
2
  #
3
3
  # Workflow customization surface for skf-verify-stack.
4
- # Team overrides: _bmad/custom/skf-verify-stack.toml under {project-root}
5
- # Personal overrides: _bmad/custom/skf-verify-stack.user.toml under {project-root}
4
+ # Team overrides: {project-root}/_bmad/custom/skf-verify-stack.toml
5
+ # Personal overrides: {project-root}/_bmad/custom/skf-verify-stack.user.toml
6
6
 
7
7
  [workflow]
8
8
 
@@ -10,27 +10,41 @@
10
10
  # scalars: override wins • arrays (persistent_facts, activation_steps_*): append
11
11
  # arrays-of-tables with `code`/`id`: replace matching items, append new ones.
12
12
 
13
- # Steps to run before the standard activation (uv probe, config load).
14
- # Overrides append. Use for org-wide pre-flight checks (auth, network,
15
- # compliance) that must precede any verification work.
13
+ # Steps SKILL.md On Activation step 4 runs once this file resolves: after
14
+ # config.yaml is loaded and {headless_mode} is resolved, before the emitter
15
+ # pre-flight (step 5) and before any stage. Overrides append. Use for org-wide
16
+ # pre-flight checks (auth, network, compliance) that must precede any
17
+ # verification work.
16
18
 
17
19
  activation_steps_prepend = []
18
20
 
19
- # Steps to run after activation but before the first stage executes.
20
- # Overrides append. Use for context loads or banner customization that
21
- # should run once activation completes successfully.
21
+ # Steps to run once activation completes (the emitter resolved), before
22
+ # references/init.md (the first stage) loads. Overrides append. Use for
23
+ # context loads or banner customization.
22
24
 
23
25
  activation_steps_append = []
24
26
 
25
27
  # Persistent facts the workflow keeps in mind for the whole run
26
28
  # (verification standards, evidence-citation rules, house-style verdict
27
- # language). Overrides append.
29
+ # language, the other names a technology goes by). Overrides append.
28
30
  #
29
- # Each entry is either:
31
+ # Each entry is one of:
30
32
  # - a literal sentence, e.g. "Verdict citations must include file:line spans."
31
33
  # - a file reference prefixed with `file:`, e.g.
32
34
  # "file:{project-root}/docs/verify-policy.md" (globs supported; file
33
35
  # contents are loaded and treated as facts).
36
+ # - an entry prefixed with `!`, which loads nothing and drops each earlier
37
+ # entry it names.
38
+ #
39
+ # Extra aliases go here: a fact such as "Our documents call the postgresql
40
+ # skill Postgres or PG." makes step 2 (coverage) pass both names to the
41
+ # mentions helper as aliases of that skill, so the architecture document's
42
+ # "PG" covers it and pairs it for step 3.
43
+ #
44
+ # The default below loads every project-context.md under {project-root}.
45
+ # Overrides append, so they cannot delete it; to stop loading those files,
46
+ # set in {project-root}/_bmad/custom/skf-verify-stack.toml:
47
+ # persistent_facts = ["!file:{project-root}/**/project-context.md"]
34
48
 
35
49
  persistent_facts = [
36
50
  "file:{project-root}/**/project-context.md",
@@ -42,18 +56,37 @@ persistent_facts = [
42
56
 
43
57
  on_complete = ""
44
58
 
45
- # --- Optional asset overrides ---
46
- #
47
- # Lift the canonical asset paths so orgs can substitute house-style copies
48
- # without forking the skill. Empty string = use the bundled default.
59
+ # --- Optional override. An empty string means the default named below. ---
49
60
 
50
- report_template_path = ""
51
- integration_rules_path = ""
52
- coverage_patterns_path = ""
61
+ # The template step 1 (references/init.md section 4) creates the feasibility
62
+ # report from: a path from the skill root, or a {project-root}/... path.
63
+ # Default: assets/feasibility-report-template.md. A house-style copy may
64
+ # reword the prose and add sections of its own, and must keep what the shared
65
+ # feasibility report schema (feasibility-report-schema.md under
66
+ # shared/references/) fixes, since the next run, create-stack-skill and
67
+ # refine-architecture read the report through it:
68
+ # - the frontmatter keys with their starting values, `schemaVersion: "1.0"`
69
+ # included;
70
+ # - the `## Executive Summary`, `## Coverage Analysis`,
71
+ # `## Integration Verdicts`, `## Recommendations` and `## Evidence Sources`
72
+ # headings, once each and in that order;
73
+ # - one empty `| lib_a | lib_b | verdict | rationale |` table under
74
+ # `## Integration Verdicts`, and the empty
75
+ # `| skill | evidence_tier | confidence_tier | metadata_schema_version | skill_md |`
76
+ # table under `## Evidence Sources`: the stages fill both in place.
77
+ # Step 6 checks the finished report against the schema and halts
78
+ # (`schema-violation`) on a copy that breaks it.
53
79
 
54
- # Override the destination directory for the feasibility report only. Empty =
55
- # use {forge_data_folder} from config.yaml. The result-contract JSON
56
- # (verify-stack-result-*.json) always stays in {forge_data_folder} as a stable
57
- # path for pipeline consumers, so redirecting this does not relocate it.
80
+ report_template_path = ""
58
81
 
59
- output_folder_path = ""
82
+ # Not overridable:
83
+ # - the integration verification rules: they hold the obligations the shared
84
+ # schema puts on this report's producer (Check 4's literal citation and the
85
+ # Plausible cap), which the report's readers rely on;
86
+ # - the coverage patterns: the shared mentions helper finds the skills the
87
+ # architecture document names, and a fact (see persistent_facts) adds the
88
+ # other names a skill goes by;
89
+ # - the report folder: the report always lands in {forge_data_folder}, where
90
+ # create-stack-skill and refine-architecture look for it. Change
91
+ # forge_data_folder in config.yaml to move the report and its readers
92
+ # together.