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,10 +1,14 @@
1
1
  ---
2
2
  nextStepFile: 'map-and-detect.md'
3
3
  outputFile: '{forge_data_folder}/analyze-source-report-{project_name}.md'
4
- heuristicsFile: '{unitDetectionHeuristicsPath}'
4
+ heuristicsFile: 'references/unit-detection-heuristics.md'
5
+ scanRootFile: 'references/scan-root.md'
5
6
  disqualifyCandidatesProbeOrder:
6
7
  - '{project-root}/_bmad/skf/shared/scripts/skf-disqualify-candidates.py'
7
8
  - '{project-root}/src/shared/scripts/skf-disqualify-candidates.py'
9
+ skillInventoryProbeOrder:
10
+ - '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
11
+ - '{project-root}/src/shared/scripts/skf-skill-inventory.py'
8
12
  detectLanguageProbeOrder:
9
13
  - '{project-root}/_bmad/skf/shared/scripts/skf-detect-language.py'
10
14
  - '{project-root}/src/shared/scripts/skf-detect-language.py'
@@ -16,7 +20,7 @@ detectLanguageProbeOrder:
16
20
 
17
21
  ## STEP GOAL:
18
22
 
19
- To classify each detected boundary from the project scan into discrete skillable units by applying detection heuristics, assigning boundary types and scope types, and filtering out disqualified candidates.
23
+ To classify each detected boundary from the project scan into discrete skillable units by applying detection heuristics, assigning boundary types and scope types, filtering out disqualified candidates and, in an interactive run with a stated goal, deferring the units outside it before export mapping.
20
24
 
21
25
  ## Rules
22
26
 
@@ -26,174 +30,156 @@ To classify each detected boundary from the project scan into discrete skillable
26
30
 
27
31
  ## MANDATORY SEQUENCE
28
32
 
33
+ Every HARD HALT in this step names its exit code, `halt_reason` and phase. When `{headless_mode}` is true it first prints its envelope on stderr through the shared emitter (`{emitEnvelopeHelper}` and `{run_dir}` come from SKILL.md On Activation): stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "mode": "interactive", "report_path": "{outputFile as an absolute path}"}`, plus `"path"` when the halt names one, then run
34
+
35
+ ```bash
36
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-analyze-source --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
37
+ ```
38
+
39
+ and display the line it prints verbatim. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. If the emitter exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing.
40
+
29
41
  ### 1. Load Context
30
42
 
31
43
  Read {outputFile} to obtain:
32
- - Project Scan results (detected boundaries, manifests, entry points)
33
- - `forge_tier` from frontmatter
34
- - `existing_skills` from frontmatter
44
+ - Project Scan results (detected boundaries, manifests, service configurations), grouped by project path
45
+ - `project_paths`, `scan_roots` (each project path's scan root: the folder every helper of this step reads), `refs`, `forge_tier`, `intent_hint`, `existing_skills` and `existing_briefs` from frontmatter
46
+
47
+ **A scan root that is gone.** A session that resumed this report runs in a new run folder, and the folder of the session that made a copy may be gone. When a recorded scan root no longer exists, make it again by {scanRootFile} into this run's `{run_dir}/source-{i}` and update `scan_roots` in {outputFile}'s frontmatter. If a command there fails, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `identify-units:1`, path `{path}`): "{path} could not be fetched: {the first stderr line}."
35
48
 
36
49
  Load {heuristicsFile} for classification rules.
37
50
 
38
51
  ### 2. Apply Detection Heuristics
39
52
 
40
- **Resolve `{disqualifyCandidatesHelper}`** from `{disqualifyCandidatesProbeOrder}`; first existing path wins. HALT if no candidate exists.
53
+ **Resolve `{disqualifyCandidatesHelper}`** from `{disqualifyCandidatesProbeOrder}` and **`{skillInventoryHelper}`** from `{skillInventoryProbeOrder}`; first existing path wins for each. If one has no candidate, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `identify-units:2`): "`{the missing script}` is missing. Re-install SKF."
41
54
 
42
55
  For each detected boundary from the scan, apply the classification rules from {heuristicsFile} (loaded in §1):
43
56
 
44
- **Step A — Count detection signals:** tally the Strong / Moderate / Weak signals per its Detection Signals tables.
45
-
46
- **Step B — Classify boundary type** per its Boundary Classification section. (Composite is detected separately in §3b below — not during this initial per-boundary pass.)
47
-
48
- **Step C — Assign scope type** from that same section for the boundary's type.
57
+ **Step A: Run the disqualification helper (script).** In one call per project path it lists each boundary's files itself, applies the deterministic subset of the rules from {heuristicsFile} (file count, LoC, generated files) and looks up the signal files and the boundary's own manifest. Its output is evidence that Step B judges, not a verdict.
49
58
 
50
- **Step D — Run deterministic disqualification filter (script):**
59
+ Run it once per project path, the `{i}`-th entry of `project_paths[]`, over the boundaries found under it, with `{scan_root}` that path's scan root (never `{project-root}`, the forge workspace) and one entry per boundary, its path relative to the scan root (`.` for a boundary at the root), with forward slashes:
51
60
 
52
- Run the shared disqualification helper to apply the deterministic subset of the rules from {heuristicsFile} (file-count, LoC, generated-code paths, auto-generated header sentinels). The script collapses what was prose-orchestrated counting + path-substring + header scanning into one deterministic call.
61
+ ```bash
62
+ uv run {disqualifyCandidatesHelper} filter --boundaries - --source-root "{scan_root}" --tree-dir "{run_dir}/unit-trees/{i}" <<'SKF_BOUNDARIES'
63
+ [{"name": "<boundary folder name>", "path": "<boundary path>"}, ...]
64
+ SKF_BOUNDARIES
65
+ ```
53
66
 
54
- 1. **Build the boundaries JSON** from the detected boundaries (one entry per candidate boundary). Use forward-slash paths throughout. Shape:
55
- ```json
56
- [
57
- {"name": "<unit-name>",
58
- "path": "<rel-from-analyzed-source-root (project_paths[0])>",
59
- "files": ["<rel-path>", ...]},
60
- ...
61
- ]
62
- ```
63
- 2. **Invoke the script** via stdin:
64
- ```bash
65
- uv run {disqualifyCandidatesHelper} filter --boundaries - --source-root {project_paths[0]}
66
- ```
67
- piping the boundaries JSON on stdin. `--source-root` is the analyzed-source root (`project_paths[0]`) — the directory the boundaries/manifest scan ran against — not `{project-root}` (the forge workspace), which differs whenever the analyzed target lives outside the forge workspace. The script emits:
67
+ 1. The script lists every boundary's files (what git lists under its path, so ignored build output and installed dependencies stay out), writes each list to the run folder and emits one record per boundary:
68
68
  ```json
69
69
  {
70
- "kept": [{"name": "...", "path": "...", "files_count": N, "loc_total": L}, ...],
71
- "dropped": [{"name": "...", "reason": "<too-few-files|too-low-loc|generated-code|auto-generated-tag>", "context": {...}}, ...],
72
- "stats": {"kept": N, "dropped": N, "by_reason": {"<reason>": N, ...}}
70
+ "kept": [{"name": "...", "path": "...", "files_count": N, "loc_total": L, "non_source_count": K,
71
+ "generated_files": [{"path": "...", "reason": "generated-code|auto-generated-tag", "match": "..."}],
72
+ "generated_ratio": R, "passes_with_generated": true,
73
+ "manifest": {"path": "...", "ecosystem": "npm", "name": "@acme/auth", "private": false},
74
+ "signals": {...}, "tree_file": "..."}, ...],
75
+ "dropped": [{"name": "...", "reason": "<too-few-files|too-low-loc|generated-code|auto-generated-tag>", "context": {...}, ...}, ...],
76
+ "stats": {"kept": N, "dropped": N, "mixed": N, "by_reason": {"<reason>": N, ...}}
73
77
  }
74
78
  ```
75
- 3. **Parse the JSON result** and stash `kept[]` and `dropped[]` in workflow state for §3 (classification table) and §5 (recommendation summary). The `kept` set is the candidate pool for the boundary-type + scope-type classification that follows; the `dropped` set drives the Disqualification table.
76
-
77
- **LLM-judged disqualifications (not in script — apply on top of `kept[]`):**
78
- - **Pure configuration** — only config files (e.g., `.json`/`.yaml`) with no executable logic
79
- - **Test-only** — test utilities with no production code
80
- - **Already skilled** — exists in `existing_skills` list (recommend `update-skill` instead)
79
+ A `dropped[]` record carries every `kept[]` field plus `reason` and `context`. Read `files_count` and `loc_total` (the boundary's own source files, counted as {heuristicsFile}'s Disqualification Rules say), `generated_files` and `generated_ratio` (the files Step B weighs), `passes_with_generated` (whether the size rules pass with those files counted too), `manifest` (the boundary's own manifest, null without one), `signals` (Step D) and `tree_file` (the boundary's file list, which §4 hands to the language helper).
80
+ 2. A boundary path that is no folder under the scan root makes the script exit 1 and name it on stderr: correct that entry and run the call again.
81
+ 3. **Stash the results** in workflow state, each record with its project path: `kept[]` is the candidate pool for the classification that follows, and `dropped[]` drives the Disqualification table.
81
82
 
82
- Remove any boundary that fails one of these LLM-judged rules from the working `kept` set and append it to `dropped[]` with the appropriate reason. Reasons recorded by the script (`too-few-files`, `too-low-loc`, `generated-code`, `auto-generated-tag`) are authoritative — do not re-evaluate those rules manually.
83
+ **Step B: Judge what the script cannot.**
83
84
 
84
- **Qualification check:** Visually skim the script's `kept`/`dropped` decisions for sanity (e.g., a boundary you expected to qualify that landed in `dropped` — surface the script's `reason` and `context.first_match` to the user in §5 so they can override if the heuristic was wrong for this project).
85
-
86
- ### 3. Build Unit Classification Table
87
-
88
- For each candidate that passes disqualification:
85
+ - **Mixed cases** (`stats.mixed` counts them): a kept boundary whose `generated_files` is not empty. Read the flagged paths and their `match`. Build output or a vendored copy beside the unit's own code leaves the unit standing, and its counts already leave those files out. Drop a mixed boundary only when its own code is no more than glue around the generated files, with reason `generated-code` and one line on why.
86
+ - **A drop the flagged files caused:** a boundary dropped as `generated-code`, `auto-generated-tag`, `too-few-files` or `too-low-loc` whose `generated_files` you judge to be its own code (a source folder named `build/` or `dist/`, a header its own tooling writes into code it maintains) goes back into `kept[]` when its `passes_with_generated` is true, with those files named in the notes. When it is false, the boundary stays dropped: even with those files it is too small.
87
+ - **LLM-judged disqualifications** (not in the script; apply on top of `kept[]`), removing a boundary that fails one from the working `kept` set and appending it to `dropped[]` with the reason:
88
+ - **Pure configuration**: only config files (e.g., `.json`/`.yaml`) with no executable logic
89
+ - **Test-only**: test utilities with no production code
89
90
 
90
- | # | Unit Name | Path | Boundary Type | Scope Type | Signals | Confidence | Status |
91
- |---|-----------|------|---------------|------------|---------|------------|--------|
92
- | 1 | {name} | {path} | {type} | {scope} | {signal count: strong/moderate/weak} | {high/medium/low} | {new/already-skilled} |
91
+ Show every drop and every restore in §6 with its reason and evidence (the record's `context`, `generated_ratio` and first `generated_files` path), so the user can override it; a headless run keeps the judgment made here.
93
92
 
94
- For disqualified candidates, note reason:
93
+ **Step C: Name each unit (script), then check it is not already skilled.** The helper that names every brief names the units too, so a unit's name here is the name its brief gets.
95
94
 
96
- **Disqualified:**
97
- | Path | Reason |
98
- |------|--------|
99
- | {path} | {disqualification reason} |
95
+ 1. Name every unit in one call, by {heuristicsFile}'s Unit Names, one entry per kept boundary straight from its record: `target` is its `path` (its project path itself for a boundary at the root), and `manifest_name` and `private` are its `manifest.name` and `manifest.private` (null when `manifest` is null):
96
+ ```bash
97
+ uv run {skillInventoryHelper} derive-name --from - <<'SKF_UNIT_NAMES'
98
+ [{"target": "<boundary path>", "manifest_name": <manifest.name>, "private": <manifest.private>}, ...]
99
+ SKF_UNIT_NAMES
100
+ ```
101
+ Each `names[].name` is that unit's name from here on. Boundaries whose names would clash are already told apart by their parent folders (`clash` keeps the name they shared); give the entries `unnamed` and `duplicates` list a name of your own, from their folder.
102
+ 2. **Already skilled:** a unit whose name is in `existing_skills` (a compiled skill) fails the last LLM-judged rule: remove it from the working `kept` set and append it to `dropped[]` with reason `already-skilled` (recommend `update-skill` instead). A unit whose name is in `existing_briefs` only has a brief from an earlier analysis: it stays a candidate with status `briefed` and its brief's path, and generate-briefs rewrites that brief only if the unit is confirmed.
100
103
 
101
- ### 3b. Detect Composite Unit Merges
104
+ **Step D: Tally detection signals** per the Detection Signals tables in {heuristicsFile}: cite the file each record's `signals` entry names (and `large_directory`), and judge the signals the helper does not report, as that file says.
102
105
 
103
- After building the classification table, apply the Composite Boundary detection heuristic from {heuristicsFile} against the qualifying units:
106
+ **Step E: Classify boundary type** per its Boundary Classification section, and **assign the scope type** from that same section for the boundary's type. This pass classifies each boundary on its own: composite merges are proposed in map-and-detect, once the import graph exists.
104
107
 
105
- 1. **Scan for merge candidates:** Among the qualifying units (from `kept[]`), find groups of ≥2 Package or Module boundaries that meet either Composite trigger — **Mutual hard dependency** or **Shared integration surface** — as defined in {heuristicsFile}'s Composite Boundary heuristic.
108
+ ### 3. Build Unit Classification Table
106
109
 
107
- 2. **If candidate groups are found**, propose each merge:
108
- - Derive a composite name from the common namespace prefix or repo name
109
- - List the constituents (boundary names and paths being merged)
110
- - State the triggering heuristic and evidence
110
+ For each candidate that passes disqualification:
111
111
 
112
- 3. **If no candidate groups are found**, skip to §4.
112
+ | # | Unit Name | Project Path | Path | Boundary Type | Scope Type | Files | Own Manifest | Signals | Confidence | Status |
113
+ |---|-----------|--------------|------|---------------|------------|-------|--------------|---------|------------|--------|
114
+ | 1 | {name} | {its project path} | {path} | {type} | {scope} | {files_count} | {manifest path, name and ecosystem, or --} | {signal count: strong/moderate/weak} | {high/medium/low} | {new/briefed/deferred} |
113
115
 
114
- **Merge does not fire for:** Units already flagged as Stack Skill Candidates in step 4 (map-and-detect §5) — those are multi-unit groupings that deliver value *separately* but are *also* useful together. Composite merges are for units that are *only* useful together (the key distinction). If a group of units is independently useful but commonly combined, it remains as separate units and is flagged as a stack skill candidate later.
116
+ For disqualified candidates, note reason and evidence:
115
117
 
116
- **This step is a recommendation — not automatic.** Merges are presented to the user in §5 for confirmation (see "Composite Merge Proposals" below). If the user rejects a merge, the constituents remain as separate units in the classification table.
118
+ **Disqualified:**
119
+ | Path | Reason | Evidence |
120
+ |------|--------|----------|
121
+ | {path} | {disqualification reason} | {the record's `context`, or `generated_ratio` and the first `generated_files` path} |
117
122
 
118
123
  ### 4. Detect Primary Language Per Unit
119
124
 
120
- For each qualifying unit (including any approved composites from §3b), detect the primary language deterministically via the shared helper — the single source of truth for the manifest→language rule table (no in-prose restatement, which drifts from the script's tsconfig JS-vs-TS and `build.gradle` Java-vs-Kotlin disambiguation).
125
+ For each qualifying unit, detect the primary language deterministically via the shared helper, the single source of truth for the manifest→language rule table.
121
126
 
122
- **Resolve `{detectLanguageHelper}`** from `{detectLanguageProbeOrder}`; first existing path wins.
127
+ **Resolve `{detectLanguageHelper}`** from `{detectLanguageProbeOrder}`; first existing path wins. If neither exists, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `identify-units:4`): "`skf-detect-language.py` is missing. Re-install SKF."
123
128
 
124
- For each unit, pipe its file list — the `files` array built for that boundary in the §2 boundaries JSON — as the tree:
129
+ For each unit, hand the helper the file list §2 wrote for it, its record's `tree_file`:
125
130
 
126
131
  ```bash
127
- echo '{"tree": [<unit files — forward-slash, repo-relative>]}' | uv run {detectLanguageHelper}
132
+ uv run {detectLanguageHelper} --tree-file "{the unit's tree_file}"
128
133
  ```
129
134
 
130
- Read `.language` and `.confidence` for the unit. When confidence is low (the extension-frequency fallback fired — no manifest matched), surface it in §5 so the user can override the guess.
131
-
132
- ### 5. Present Classifications
133
-
134
- "**Unit Identification Complete**
135
+ Pass no `--workspace-signal` here: a workspace root's language would answer for every unit of the workspace (a TypeScript package in a Cargo workspace would read as `rust`). The unit's folder is the tree's root for the helper, so the unit's own manifest decides, and a manifest in its docs, examples, tests or tools folder never does while another one exists. Read `.language` and `.confidence` for the unit. When `detected_languages` has more than one entry, `.language` is the helper's first guess: choose the language the unit documents from its own manifest (the record's `manifest`), or, without one, `.source_language` (the language most of its files are written in), and name the choice and the other languages in §6. When confidence is low (the extension-frequency fallback fired: no manifest matched), surface it in §6 so the user can override the guess.
135
136
 
136
- **Qualifying Units:** {count}
137
+ ### 5. Defer What the Goal Leaves Out
137
138
 
138
- {Classification table}
139
+ Interactive runs only, and only when `intent_hint` is not empty: mark each qualifying unit the stated goal clearly leaves out `deferred` in the Status column, with one line on why (for example "deferred: payments, outside the stated goal (authentication)"). Step 4 maps only the units that are not deferred, and the deferred ones stay in the classification table, so nothing is dropped without notice. A unit you are unsure about stays in. A headless run defers nothing: there `intent_hint` only ranks the step 5 recommendations.
139
140
 
140
- **Disqualified Candidates:** {count}
141
- {Disqualification table}
141
+ ### 6. Present the Classifications and Confirm
142
142
 
143
- **Already-Skilled Units:** {count from existing_skills match}
144
- {List with recommendation to run update-skill if source has changed}
143
+ Show the user the classification table, then:
145
144
 
146
- {IF composite merge proposals exist from §3b:}
145
+ - **Mixed cases** (`stats.mixed`): for each qualifying unit with generated files, its name, `generated_ratio`, the flagged folders or headers and why it stays; and each boundary §2 Step B restored, with the files that restored it
146
+ - **Disqualified candidates**, with the table above
147
+ - **Already-skilled units** (from `existing_skills`), each with a recommendation to run update-skill if its source changed
148
+ - **Deferred units**, when §5 deferred any, with how to restore them
149
+ - **Notes:** structure patterns observed, and ambiguous boundaries that need the user's call
147
150
 
148
- **Composite Merge Proposals:** {count}
151
+ Then display:
149
152
 
150
- | # | Composite Name | Constituents | Heuristic | Evidence |
151
- |---|----------------|--------------|-----------|----------|
152
- | 1 | {name} | {list of constituent unit names} | {mutual hard dependency / shared integration surface} | {brief evidence} |
153
+ "Add, remove, reclassify or restore any unit? Tell me, or **Select:** [C] Continue to Export Mapping and Integration Detection | [X] Cancel and exit"
153
154
 
154
- If approved, each composite replaces its constituents in the classification table as a single Composite Boundary unit. The constituents are recorded in the composite's metadata for downstream workflows (create-skill reads constituents to scope extraction across all member paths).
155
-
156
- {END IF}
155
+ #### Menu Handling Logic:
157
156
 
158
- **Notes:**
159
- - {Any observations about project structure patterns}
160
- - {Any ambiguous boundaries that need user clarification}
157
+ - IF C: go to §7
158
+ - IF X: HARD HALT (exit code 6, `halt_reason: "user-cancelled"`, phase `identify-units:6`): "Cancelled at unit identification."
159
+ - IF Any other: adjust the classifications as directed (restoring a deferred unit sets its status back to new or briefed), show what changed, then [Redisplay Menu Options](#6-present-the-classifications-and-confirm)
161
160
 
162
- Do these classifications look correct? Should any units be added, removed, or reclassified?
163
- {IF composites proposed:} Are the composite merge proposals correct? (Accept/reject each individually.)"
161
+ **GATE [default: C]**: present the menu and wait for the user's choice. If `{headless_mode}`: accept every classification and continue with [C], and record that decision the moment it is taken: stage `{run_dir}/decision.json` as `{"gate": "identify-units.classifications", "default_action": "C", "taken_action": "C", "reason": "headless: auto-accept unit classifications", "evidence": {"qualifying": <the qualifying unit count>, "dropped": <the dropped boundary count>}}`, then run
164
162
 
165
- Wait for user feedback. Adjust classifications based on user input. For approved composites: remove the constituent rows from the qualifying units table and add a single composite row with `Boundary Type: Composite`, scope type inherited from the dominant constituent, and confidence reflecting the merge heuristic strength.
163
+ ```bash
164
+ uv run {emitEnvelopeHelper} record --workflow skf-analyze-source --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"
165
+ ```
166
166
 
167
- ### 6. Append to Report
167
+ If the command fails, go on: only that entry is lost.
168
168
 
169
- Append the complete "## Identified Units" section to {outputFile}:
169
+ ### 7. Append to Report and Continue
170
170
 
171
- Replace the placeholder `[Appended by identify-units]` with:
172
- - Classification table (qualifying units, including approved composites)
173
- - Composite merge details (if any): composite name, constituents list, heuristic, evidence
171
+ Replace the placeholder `[Appended by identify-units]` in {outputFile} with:
172
+ - Classification table (qualifying units, under their §2 Step C names, with their project paths, file counts, own manifests and statuses, deferred units included)
173
+ - Mixed cases and restored boundaries, with their evidence
174
174
  - Disqualification table
175
175
  - Already-skilled units list
176
- - Language detection results
176
+ - Language detection results, and each unit's `tree_file`
177
177
  - Any user adjustments noted
178
178
 
179
179
  Update {outputFile} frontmatter:
180
180
  ```yaml
181
181
  stepsCompleted: [append 'identify-units' to existing array]
182
182
  lastStep: 'identify-units'
183
- confirmed_composites: [{list of approved composite merge objects: {name, constituents[], heuristic}}]
184
183
  ```
185
184
 
186
- (`confirmed_composites` is an empty array when no composites were proposed or all were rejected.)
187
-
188
- ### 7. Present MENU OPTIONS
189
-
190
- Display: "**Select:** [C] Continue to Export Mapping and Integration Detection | [X] Cancel and exit"
191
-
192
- #### Menu Handling Logic:
193
-
194
- - IF C: Save classifications to {outputFile}, update frontmatter, then load, read entire file, then execute {nextStepFile}
195
- - IF X: HARD HALT with exit code 6 (`user-cancelled`). Emit the error envelope on stderr with `halt_reason: "user-cancelled"` and counts/paths reflecting state at cancellation (shape in `references/headless-contract.md`)
196
- - IF Any other: help user, then [Redisplay Menu Options](#7-present-menu-options)
197
-
198
- **GATE [default: C]** — present the menu and wait for the user's choice. If `{headless_mode}`: accept all classifications and auto-proceed, log: "headless: auto-accept unit classifications".
199
-
185
+ Then load, read the entire file, then execute {nextStepFile}.
@@ -3,6 +3,9 @@ nextStepFile: 'scan-project.md'
3
3
  continueFile: 'continue.md'
4
4
  outputFile: '{forge_data_folder}/analyze-source-report-{project_name}.md'
5
5
  templateFile: '{analysisReportTemplatePath}'
6
+ skillInventoryProbeOrder:
7
+ - '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
8
+ - '{project-root}/src/shared/scripts/skf-skill-inventory.py'
6
9
  ---
7
10
 
8
11
  <!-- Config: communicate in {communication_language}. -->
@@ -11,30 +14,45 @@ templateFile: '{analysisReportTemplatePath}'
11
14
 
12
15
  ## STEP GOAL:
13
16
 
14
- To initialize the analyze-source workflow by loading configuration, detecting continuation state, accepting the target project path, checking for existing skills, and creating the analysis report document.
17
+ To initialize the analyze-source workflow by loading configuration, detecting continuation state, taking the target project path, the analysis goal and the scope from the invocation or one opening question, checking for existing skills, and creating the analysis report document.
15
18
 
16
19
  ## Rules
17
20
 
18
- - Focus only on initialization — do not begin scanning or analysis
19
- - Collect project path and scope hints from user
21
+ - Focus only on initialization: do not begin scanning or analysis
22
+ - Use what the invocation already says before asking anything, and ask at most one opening question
20
23
  - Verify prerequisites before proceeding
21
24
 
22
25
  ## MANDATORY SEQUENCE
23
26
 
24
- When `{headless_mode}` is true, every HARD HALT in this step emits the error envelope on **stderr** before exiting — shape and enum in `references/headless-contract.md` — using the exit code and `halt_reason` named at that HALT.
27
+ Every HARD HALT in this step names its exit code, `halt_reason` and phase. When `{headless_mode}` is true it first prints its envelope on stderr through the shared emitter (`{emitEnvelopeHelper}` and `{run_dir}` come from SKILL.md On Activation): stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "mode": "<auto when the invocation carries [auto], else interactive>"}`, plus `"path"` when the halt names one, then run
28
+
29
+ ```bash
30
+ uv run {emitEnvelopeHelper} emit-halt --workflow skf-analyze-source --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
31
+ ```
32
+
33
+ and display the line it prints verbatim. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. If the emitter exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing.
25
34
 
26
35
  ### 1. Check for Existing Report (Continuation Detection)
27
36
 
28
37
  Look for {outputFile}.
29
38
 
30
- **IF the file exists AND has `stepsCompleted` with entries:**
31
- - The report filename is keyed to `{project_name}` (the forge workspace), not the analyzed target — so a report from a *different* target can collide here. Before resuming, establish the requested target and compare it to the existing report:
32
- - Determine the requested target now: if `--project-path <path>` was passed at invocation, set `project_paths[]` from it (comma-split if multiple); otherwise collect the path(s) using the section-3 "Collect Project Path" prompt and store as `project_paths[]`. (Section 3 does not re-prompt when `project_paths[]` is already populated here.)
33
- - Read the existing report's frontmatter `project_paths`.
34
- - **IF the existing report's `project_paths` matches the requested target:**
35
- - **IF this invocation carries the `[auto]` flag (e.g. `AN[auto]`):** do not route to {continueFile} — auto mode is a single idempotent pass, not a resumable interactive session. Skip continuation and proceed to section 2; the `[auto]` check in §2b re-enters step-auto-scope.md, which re-runs cleanly and overwrites the prior auto report. (This keeps a re-invoked or interrupted auto run on the auto path instead of dropping it into the interactive chain.)
36
- - **ELSE:** "**Found an existing analysis report. Resuming previous session...**" — Load, read entirely, then execute {continueFile}. **STOP HERE** — do not continue this sequence. ({continueFile} is mode-aware: a report written by a prior auto run resumes through the auto path, not the interactive chain.)
37
- - **ELSE (different target — stale collision):** the existing report belongs to another analysis. Archive it by renaming to `{forge_data_folder}/analyze-source-report-{project_name}-<UTC-timestamp>.md`, announce "**Existing report belongs to a different target — archived as <name>; starting a fresh analysis.**", then continue to section 2 (skip re-collecting the path in section 3 — it is already set).
39
+ **IF the file exists AND has `stepsCompleted` with entries:** the report filename is keyed to `{project_name}` (the forge workspace), not the analyzed target, so the report may belong to another analysis or to one that already ended. Apply the first rule that holds:
40
+
41
+ 1. **Finished:** `stepsCompleted` holds `generate-briefs` or `auto-scope`: that analysis already ended (with its briefs, or with none). Archive it (below), announce "**The previous analysis of this workspace is finished: archived as <name>; starting a fresh analysis.**", and continue to section 2. A finished report is never resumed, in headless mode either.
42
+ 2. **Auto invocation or auto report:** this invocation carries the `[auto]` flag (e.g. `AN[auto]`), or the report's `mode` is `'auto'` (an `[auto]` run that was interrupted or ended at step 1a's coexistence gate with [M]erge or [S]kip). Auto mode is one pass that never resumes, so archive the unfinished report the same way, announce "**An `[auto]` analysis never resumes: archived the unfinished report as <name>; starting a fresh analysis.**", and continue to section 2.
43
+ 3. **Unfinished:** establish the requested target and compare it to the report's. Take `project_paths[]` from the invocation as section 3 says (`--project-path`, comma-split, or a path the message names); when it gives none, ask section 3's opening question now (a headless run halts there instead), once: section 3 then asks nothing more. Read the report's frontmatter `project_paths`.
44
+ - **Same target:** compare the ref and hint flags this invocation passes with the report's frontmatter: `--target-ref` or `--target-refs`, resolved to each project path's ref as section 4 does, against `refs`, and `--scope-hint` and `--intent-hint` against `scope_hint` and `intent_hint`. A flag the invocation does not pass changes nothing.
45
+ - **The inputs match:** "**Found an unfinished analysis report. Resuming the previous session...**" Load, read entirely, then execute {continueFile}. **STOP HERE**: do not continue this sequence. Headless runs resume it too.
46
+ - **An input differs:** the later steps would read the report's old values, so archive it and announce "**The inputs changed: archived as <name>; starting a fresh analysis.**" In headless mode, also run `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning 'inputs changed: the unfinished report was archived as <name>'`, a single quote in it written as a backtick (if it fails, go on). Then continue to section 2: section 3 keeps the path already set, and section 4 resolves the refs again.
47
+ - **Different target (stale collision):** archive it, announce "**Existing report belongs to a different target: archived as <name>; starting a fresh analysis.**", then continue to section 2 (section 3 keeps the path already set).
48
+
49
+ **To archive a report** (rules 1 and 2, changed inputs and a different target; a report that resumes stays where it is), rename it, never overwrite it:
50
+
51
+ ```bash
52
+ archive="{forge_data_folder}/analyze-source-report-{project_name}-$(date -u +%Y%m%d-%H%M%S).md" && mv -n "{outputFile}" "$archive" && printf '%s\n' "$archive"
53
+ ```
54
+
55
+ and use the path it prints as `<name>`. If the rename fails, HARD HALT (exit code 4, `halt_reason: "write-failed"`, phase `init:1`, path `{outputFile}`): "The earlier analysis report could not be archived: {the first stderr line}."
38
56
 
39
57
  **IF the file does not exist OR stepsCompleted is empty:**
40
58
  - Continue to section 2
@@ -43,7 +61,7 @@ Look for {outputFile}.
43
61
 
44
62
  **Check forge-tier.yaml:**
45
63
  - Look for `{sidecar_path}/forge-tier.yaml`
46
- - **IF missing:** HARD HALT — "**Cannot proceed.** forge-tier.yaml not found at `{sidecar_path}/forge-tier.yaml`. Please run the setup workflow first to configure your forge tier (Quick/Forge/Forge+/Deep)."
64
+ - **IF missing:** HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `init:2`, path `{sidecar_path}/forge-tier.yaml`): "**Cannot proceed.** forge-tier.yaml not found at `{sidecar_path}/forge-tier.yaml`. Please run the setup workflow first to configure your forge tier (Quick/Forge/Forge+/Deep)."
47
65
  - **IF found:** Read and note the forge tier value
48
66
 
49
67
  **Apply tier override:** Read `{sidecar_path}/preferences.yaml`. If `tier_override` is set and is a valid tier value (Quick, Forge, Forge+, or Deep), use it instead of the detected tier.
@@ -56,9 +74,10 @@ Look for {outputFile}.
56
74
 
57
75
  **IF `{auto_mode}` is true:**
58
76
 
59
- 1. **Resolve project path:** If `project_paths[]` is already populated (from §1 continuation detection or `--project-path` arg), use it. Otherwise, if `--project-path <path>` was passed at invocation, set `project_paths[]` from it (comma-split if multiple). If neither is available, HARD HALT with exit code 2 (`input-missing`): "**Auto mode requires `--project-path` — no project path available.**"
60
- 2. **Validate the path(s):** For each provided path/URL, check that it exists (local) or is accessible (remote). If any invalid: HARD HALT with exit code 3 (`resolution-failure`): "**Path `{path}` doesn't appear to be valid.**"
61
- 3. **Create the analysis report** from {templateFile}. Populate frontmatter:
77
+ 1. **Resolve project path:** If `project_paths[]` is already populated (from §1 continuation detection or `--project-path` arg), use it. Otherwise, if `--project-path <path>` was passed at invocation, set `project_paths[]` from it (comma-split if multiple). If neither is available, HARD HALT (exit code 2, `halt_reason: "input-missing"`, phase `init:2b`): "**Auto mode requires `--project-path`: no project path available.**"
78
+ 2. **Validate the path(s):** For each provided path/URL, check that it exists (local) or is accessible (remote). If any invalid: HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `init:2b`, path `{path}`): "**Path `{path}` doesn't appear to be valid.**"
79
+ 3. **Take the hints:** `intent_hint` ← the `--intent-hint` value and `scope_hint` ← the `--scope-hint` value (empty when the flag is absent). Step 1a reads both, and so does the interactive chain it may fall back to.
80
+ 4. **Create the analysis report** from {templateFile}. First build `existing_skills` and `existing_briefs` as section 5 does, without its messages: the step-by-step analysis an unclassified repository falls back to reads them. Populate frontmatter:
62
81
  ```yaml
63
82
  stepsCompleted: ['init']
64
83
  lastStep: 'init'
@@ -68,75 +87,52 @@ Look for {outputFile}.
68
87
  project_name: '{project_name}'
69
88
  project_paths: ['{provided_project_path}']
70
89
  forge_tier: '{detected_tier}'
71
- existing_skills: []
90
+ existing_skills: [{list of existing skill names}]
91
+ existing_briefs: [{name and path of each existing brief, as {name: '...', path: '...'}}]
92
+ intent_hint: '{intent_hint}'
93
+ scope_hint: '{scope_hint}'
72
94
  confirmed_units: []
73
95
  stack_skill_candidates: []
74
96
  nextWorkflow: ''
75
97
  mode: 'auto'
76
98
  ```
77
- 4. "**Auto mode activated — bypassing interactive analysis.**"
78
- 5. **Route to auto-scope:** Load, read fully, then execute `references/step-auto-scope.md`. **STOP HERE** — do not continue to §3 or any subsequent section.
79
-
80
- **IF `{auto_mode}` is not true:**
81
- Continue to §3 as normal — the entire interactive flow below is unchanged.
82
-
83
- ### 3. Collect Project Path
99
+ 5. "**Auto mode activated: bypassing interactive analysis.**"
100
+ 6. **Route to auto-scope:** Load, read fully, then execute `references/step-auto-scope.md`. **STOP HERE**: do not continue to §3 or any subsequent section.
84
101
 
85
- **Headless flag consumption:** If `project_paths[]` is already populated (e.g. collected by the section-1 stale-collision guard) OR `--project-path <path>` was passed at invocation, set/keep `project_paths[]` (comma-split the flag value if multiple paths were supplied), skip the prompt below, and proceed to validation. If `{headless_mode}` is true and no path is available from either source, HARD HALT with exit code 2 (`input-missing`): "**No project path — headless mode requires `--project-path`.**" (interactive prompting is unavailable headless). Otherwise prompt as today.
102
+ **IF `{auto_mode}` is not true:** continue to §3.
86
103
 
87
- **Per-path ref overrides (`--target-refs`):** If `--target-refs <mapping>` was passed at invocation, parse it as a comma-separated list of `path:ref` pairs (e.g., `owner/repo:v1.0.0,owner/repo2:main`). Build a `constituent_refs` map from the pairs. Each key must match an entry in `project_paths[]` (validated after path collection). When `--target-refs` is absent but multiple `project_paths` exist, set `constituent_refs` to `{}` (empty — all paths use default ref resolution). When only a single path exists, omit `constituent_refs` entirely (use `target_ref` if set on the brief). `constituent_refs` and `target_ref` are mutually exclusive — if both are supplied, HALT with: "`--target-refs` and `--target-ref` are mutually exclusive. Use `--target-refs` for multi-path analysis, or `--target-ref` for single-path."
104
+ ### 3. Opening Question
88
105
 
89
- "**Please provide the project root path(s) to analyze** — I'll identify discrete skillable units and produce a skill-brief.yaml for each.
106
+ Read the invocation first: its flags (`--project-path`, comma-split for several paths; `--intent-hint`; `--scope-hint`) and, in an interactive run, its message, which may name a path or URL, the goal of the analysis, or folders and packages to focus on or skip ("analyze /work/mono, I only care about auth, skip vendor/"). A flag wins over the message. `project_paths[]` set by section 1 stays as it is.
90
107
 
91
- This can be:
92
- - A single root directory of a repo or multi-service project
93
- - Multiple paths or URLs (comma-separated) for multi-repo analysis (e.g., integration/stack skills)
108
+ - `intent_hint`: the goal, in the user's words (a domain such as authentication, the consumers the skills serve, a constraint such as stable public APIs only).
109
+ - `scope_hint`: the folders or packages to focus on, and those to skip.
94
110
 
95
- Examples:
96
- - `/path/to/project`
97
- - `owner/repo, owner/repo2`
98
- - `/path/to/project, https://github.com/owner/repo2`"
111
+ **Headless** (`{headless_mode}` true): ask nothing. With no path from a flag or section 1, HARD HALT (exit code 2, `halt_reason: "input-missing"`, phase `init:3`): "**No project path: headless mode requires `--project-path`.**" A hint no flag gave is empty: a headless run never reads one from the message.
99
112
 
100
- Wait for user input.
113
+ **Interactive:** ask at most one question, and only for what is still missing:
101
114
 
102
- **Validate the path(s):**
103
- - For each provided path/URL: check that it exists (local) or is accessible (remote)
104
- - **IF any invalid:** "Path `{path}` doesn't appear to be valid. Please correct it."
105
- - Store as `project_paths[]` array in report frontmatter (single path stored as 1-element array for consistency)
106
- - **IF `constituent_refs` was built from `--target-refs`:** Validate that every key in the map matches an entry in `project_paths[]`. If any key has no matching path, HALT: "constituent_refs key `{key}` does not match any entry in project_paths."
115
+ - **No path yet:** "**Which project should I analyze?** Give the root path(s) or URL(s), comma-separated for several repositories (`/path/to/project`, `owner/repo, owner/repo2`). Optionally add what you want out of it and anything to focus on or skip (for example: auth packages only, skip `vendor/`)."
116
+ - **A path, but no goal:** "**What should this analysis focus on, or keep out?** A domain, packages, folders to skip. Press Enter to analyze everything."
117
+ - **A path and a goal:** ask nothing.
107
118
 
108
- **Collect intent hint** (drives recommendation ranking in Step 5):
119
+ Wait for the answer, then fill what is still missing from it: `project_paths[]`, `intent_hint` and `scope_hint`, each empty when the answer gives none. A value the invocation gave stays.
109
120
 
110
- **Headless flag consumption:** If `--intent-hint <text>` was passed at invocation, set workflow-context `intent_hint` directly from the flag value, skip the prompt below, and proceed. If `{headless_mode}` is true and no `--intent-hint` was supplied, set `intent_hint = ""` (empty) and proceed without prompting.
121
+ ### 4. Validate the Inputs
111
122
 
112
- "**Optional: What are you hoping to get out of this analysis?**
123
+ **Paths:** for each entry of `project_paths[]`, check that it exists (local) or is accessible (remote). An invalid one: "Path `{path}` doesn't appear to be valid. Please correct it." and wait for the corrected path. In headless mode no one can correct it: HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `init:4`, path `{path}`) with that message.
113
124
 
114
- For example:
115
- - Skills for a specific domain (e.g., 'authentication and authorization')
116
- - Target consumer agents (e.g., 'skills our backend team's AI assistants will call')
117
- - Constraints (e.g., 'we only want stable public APIs, no internal modules')
125
+ **Ref pins:** `--target-ref <ref>` names one git tag or branch for every project path: `target_ref` ← it. `--target-refs <mapping>` names one per path, as comma-separated `path:ref` pairs (`owner/repo:v1.0.0,owner/repo2:main`), and every key must be an entry of `project_paths[]`, else HARD HALT (exit code 2, `halt_reason: "input-missing"`, phase `init:4`): "`--target-refs` key `{key}` does not match any entry in project_paths." The two are mutually exclusive: when both are passed, HARD HALT (exit code 2, `halt_reason: "input-missing"`, phase `init:4`): "`--target-refs` and `--target-ref` are mutually exclusive. Use `--target-refs` for one ref per path, or `--target-ref` for one ref for every path." Resolve each project path's **ref** once, here: its `--target-refs` entry, else `target_ref`, else none (the default ref: HEAD for a local path, the default branch for a remote one). `refs` ← each project path that has a ref, mapped to it (`{}` when none has one). Every later step reads a path's ref from `refs` alone: scan-project reads each path at its ref, and generate-briefs writes each unit's ref into its brief as `target_ref`.
118
126
 
119
- Type details, or press Enter to skip."
120
-
121
- Wait for user input. Store as workflow-context `intent_hint` (empty string if skipped).
122
-
123
- ### 4. Collect Optional Scope Hints
124
-
125
- **Headless flag consumption:** If `--scope-hint <text>` was passed at invocation, set workflow-context `scope_hint` directly from the flag value, skip the prompt below, and proceed. If `{headless_mode}` is true and no `--scope-hint` was supplied, set `scope_hint = ""` (empty) and proceed without prompting.
126
-
127
- "**Optional: Do you have scope hints to narrow the analysis?**
128
-
129
- For example:
130
- - Specific packages to focus on (e.g., `packages/auth`, `services/api`)
131
- - Directories to exclude (e.g., `vendor/`, `node_modules/`, `dist/`)
132
-
133
- Enter scope hints, or press Enter to analyze the entire project."
127
+ ### 5. Check for Existing Skills
134
128
 
135
- Wait for user input. Document any hints provided.
129
+ A unit is already skilled when a compiled skill carries its name. A brief alone does not make it one: an earlier analysis, archived in section 1, may have written it. **Resolve `{skillInventoryHelper}`** from `{skillInventoryProbeOrder}`; first existing path wins. If neither exists, HARD HALT (exit code 3, `halt_reason: "resolution-failure"`, phase `init:5`): "`skf-skill-inventory.py` is missing. Re-install SKF." List the compiled skills:
136
130
 
137
- ### 5. Check for Existing Skills
131
+ ```bash
132
+ uv run {skillInventoryHelper} "{skills_output_folder}"
133
+ ```
138
134
 
139
- Scan `{forge_data_folder}/*/skill-brief.yaml` (one level deep — each skill has its own subdirectory) for existing skill briefs.
135
+ `existing_skills` ← the `name` of every `skills[]` entry whose `skf_skill` is true (none when it exits non-zero: no skills folder yet). A skill SKF did not generate is left out: update-skill, which an already-skilled unit is sent to, changes only the skills SKF generated. Then scan `{forge_data_folder}/*/skill-brief.yaml` (one level deep: each skill has its own subdirectory): `existing_briefs` ← `{name, path}` of each brief whose folder name is not in `existing_skills`.
140
136
 
141
137
  **IF existing skills found:**
142
138
  "**Existing skills detected:**
@@ -144,12 +140,15 @@ Scan `{forge_data_folder}/*/skill-brief.yaml` (one level deep — each skill has
144
140
 
145
141
  These units will be flagged as 'already skilled' during analysis. If source changes are detected, I'll recommend running update-skill instead of generating new briefs."
146
142
 
147
- **IF no existing skills found:**
143
+ **IF existing briefs found:**
144
+ "**Briefs with no compiled skill yet:** {list each name and path}. Their units are analyzed again; a brief is rewritten only when you confirm its unit."
145
+
146
+ **IF neither found:**
148
147
  "**No existing skills found.** All identified units will be treated as new."
149
148
 
150
149
  ### 6. Create Analysis Report
151
150
 
152
- Create {outputFile} from {templateFile}. If the write fails, HARD HALT with exit code 4 (`write-failed`) per `references/headless-contract.md`.
151
+ Create {outputFile} from {templateFile}. If the write fails, HARD HALT (exit code 4, `halt_reason: "write-failed"`, phase `init:6`, path `{outputFile}`): "The analysis report could not be written: {the error}."
153
152
 
154
153
  **Populate frontmatter:**
155
154
  ```yaml
@@ -160,9 +159,11 @@ date: '{current_date}'
160
159
  user_name: '{user_name}'
161
160
  project_name: '{project_name}'
162
161
  project_paths: ['{provided_project_path}']
163
- constituent_refs: {map from --target-refs, or omit if single path}
162
+ target_ref: '{target_ref, or empty}'
163
+ refs: {each project path with a ref: its ref}
164
164
  forge_tier: '{detected_tier}'
165
165
  existing_skills: [{list of existing skill names}]
166
+ existing_briefs: [{name and path of each existing brief, as {name: '...', path: '...'}}]
166
167
  intent_hint: '{intent_hint or empty string}'
167
168
  scope_hint: '{scope_hint or empty string}'
168
169
  confirmed_units: []
@@ -170,14 +171,15 @@ stack_skill_candidates: []
170
171
  nextWorkflow: ''
171
172
  ```
172
173
 
173
- **`constituent_refs` presence rules:** Include the field only when `project_paths` has more than one entry. When present, keys are path strings matching `project_paths[]` entries, values are explicit git refs (tag/branch/commit). Paths with no explicit ref have no entry in the map (default ref resolution applies). Downstream steps (scan-project, brief generation) read this map to resolve per-constituent refs when cloning or reading off-HEAD constituents.
174
+ `target_ref` stays for a path that discover-additional-source adds later, which takes it as its ref.
174
175
 
175
176
  "**Initialization complete.**
176
177
 
177
- **Project:** {project_path}
178
+ **Project:** {project_paths, each with its ref when it has one}
178
179
  **Forge Tier:** {forge_tier}
179
180
  **Existing Skills:** {count}
180
- **Scope Hints:** {hints or 'None — full project analysis'}
181
+ **Goal:** {intent_hint, or 'None: every unit is analyzed'}
182
+ **Scope Hints:** {scope_hint, or 'None: full project analysis'}
181
183
 
182
184
  **Proceeding to project scan...**"
183
185