bmad-module-skill-forge 1.4.0 → 1.5.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 (231) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +0 -8
  3. package/docs/_data/pinned.yaml +1 -1
  4. package/docs/_internal/STABILITY.md +1 -1
  5. package/docs/architecture.md +2 -2
  6. package/docs/examples.md +1 -1
  7. package/docs/workflows.md +5 -5
  8. package/package.json +2 -2
  9. package/src/README.md +1 -1
  10. package/src/knowledge/ccc-bridge.md +12 -12
  11. package/src/knowledge/qmd-registry.md +5 -5
  12. package/src/shared/health-check.md +5 -5
  13. package/src/shared/references/description-guard-protocol.md +100 -0
  14. package/src/shared/references/output-contract-schema.md +1 -1
  15. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +6 -6
  16. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +9 -3
  17. package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +149 -0
  18. package/src/shared/scripts/schemas/skill-brief.v1.json +22 -2
  19. package/src/shared/scripts/skf-build-change-manifest.py +420 -0
  20. package/src/shared/scripts/skf-check-workspace-drift.py +321 -0
  21. package/src/shared/scripts/skf-compare-file-hashes.py +357 -0
  22. package/src/shared/scripts/skf-description-guard.py +359 -0
  23. package/src/shared/scripts/skf-detect-language.py +2 -2
  24. package/src/shared/scripts/skf-detect-scripts-assets.py +613 -0
  25. package/src/shared/scripts/skf-detect-tools.py +97 -8
  26. package/src/shared/scripts/skf-detect-workspaces.py +1 -1
  27. package/src/shared/scripts/skf-disqualify-candidates.py +576 -0
  28. package/src/shared/scripts/skf-emit-brief-result-envelope.py +3 -3
  29. package/src/shared/scripts/skf-emit-result-envelope.py +93 -9
  30. package/src/shared/scripts/skf-enumerate-stack-skills.py +514 -0
  31. package/src/shared/scripts/skf-forge-tier-rw.py +11 -11
  32. package/src/shared/scripts/skf-hash-content.py +284 -0
  33. package/src/shared/scripts/skf-load-provenance.py +295 -0
  34. package/src/shared/scripts/skf-merge-ccc-exclusions.py +45 -1
  35. package/src/shared/scripts/skf-pair-intersect.py +250 -0
  36. package/src/shared/scripts/skf-provenance-gap-dispatch.py +433 -0
  37. package/src/shared/scripts/skf-qmd-classify-collections.py +42 -8
  38. package/src/shared/scripts/skf-recommend-scope-type.py +2 -2
  39. package/src/shared/scripts/skf-render-quick-metadata.py +2 -2
  40. package/src/shared/scripts/skf-resolve-authoritative-files.py +504 -0
  41. package/src/shared/scripts/skf-scan-manifests.py +738 -0
  42. package/src/shared/scripts/skf-scan-skill-md-structure.py +375 -0
  43. package/src/shared/scripts/skf-update-active-symlink.py +315 -0
  44. package/src/shared/scripts/skf-validate-brief-inputs.py +5 -5
  45. package/src/shared/scripts/skf-validate-brief-schema.py +348 -0
  46. package/src/shared/scripts/skf-write-skill-brief.py +61 -5
  47. package/src/skf-analyze-source/SKILL.md +69 -16
  48. package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
  49. package/src/skf-analyze-source/customize.toml +55 -0
  50. package/src/skf-analyze-source/{steps-c/step-01b-continue.md → references/continue.md} +12 -12
  51. package/src/skf-analyze-source/{steps-c/step-06-generate-briefs.md → references/generate-briefs.md} +29 -10
  52. package/src/skf-analyze-source/{steps-c/step-07-health-check.md → references/health-check.md} +5 -3
  53. package/src/skf-analyze-source/{steps-c/step-03-identify-units.md → references/identify-units.md} +44 -14
  54. package/src/skf-analyze-source/{steps-c/step-01-init.md → references/init.md} +27 -6
  55. package/src/skf-analyze-source/{steps-c/step-04-map-and-detect.md → references/map-and-detect.md} +52 -25
  56. package/src/skf-analyze-source/{steps-c/step-05-recommend.md → references/recommend.md} +15 -8
  57. package/src/skf-analyze-source/{steps-c/step-02-scan-project.md → references/scan-project.md} +12 -10
  58. package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -0
  59. package/src/skf-analyze-source/templates/analysis-report-template.md +6 -6
  60. package/src/skf-audit-skill/SKILL.md +66 -15
  61. package/src/skf-audit-skill/assets/drift-report-template.md +6 -5
  62. package/src/skf-audit-skill/customize.toml +49 -0
  63. package/src/skf-audit-skill/{steps-c/step-07-health-check.md → references/health-check.md} +5 -3
  64. package/src/skf-audit-skill/references/init.md +305 -0
  65. package/src/skf-audit-skill/{steps-c/step-02-re-index.md → references/re-index.md} +11 -11
  66. package/src/skf-audit-skill/{steps-c/step-06-report.md → references/report.md} +25 -7
  67. package/src/skf-audit-skill/{steps-c/step-04-semantic-diff.md → references/semantic-diff.md} +6 -6
  68. package/src/skf-audit-skill/{steps-c/step-05-severity-classify.md → references/severity-classify.md} +5 -5
  69. package/src/skf-audit-skill/references/severity-rules.md +2 -0
  70. package/src/skf-audit-skill/{steps-c/step-03-structural-diff.md → references/structural-diff.md} +29 -16
  71. package/src/skf-brief-skill/SKILL.md +48 -18
  72. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  73. package/src/skf-brief-skill/assets/skill-brief-schema.md +32 -2
  74. package/src/skf-brief-skill/customize.toml +44 -0
  75. package/src/skf-brief-skill/{steps-c/step-02-analyze-target.md → references/analyze-target.md} +5 -5
  76. package/src/skf-brief-skill/{steps-c/step-04-confirm-brief.md → references/confirm-brief.md} +13 -10
  77. package/src/skf-brief-skill/references/draft-checkpoint.md +4 -4
  78. package/src/skf-brief-skill/{steps-c/step-01-gather-intent.md → references/gather-intent.md} +24 -24
  79. package/src/skf-brief-skill/references/headless-args.md +9 -9
  80. package/src/skf-brief-skill/references/headless-source-authority-detection.md +1 -1
  81. package/src/skf-brief-skill/{steps-c/step-06-health-check.md → references/health-check.md} +4 -2
  82. package/src/skf-brief-skill/references/portfolio-similarity-check.md +2 -2
  83. package/src/skf-brief-skill/references/qmd-collection-registration.md +1 -1
  84. package/src/skf-brief-skill/{steps-c/step-03-scope-definition.md → references/scope-definition.md} +19 -16
  85. package/src/skf-brief-skill/references/version-resolution.md +2 -2
  86. package/src/skf-brief-skill/{steps-c/step-05-write-brief.md → references/write-brief.md} +11 -10
  87. package/src/skf-create-skill/SKILL.md +25 -19
  88. package/src/skf-create-skill/assets/compile-assembly-rules.md +3 -3
  89. package/src/skf-create-skill/assets/skill-sections.md +5 -5
  90. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +11 -11
  91. package/src/skf-create-skill/customize.toml +35 -0
  92. package/src/skf-create-skill/references/authoritative-files-protocol.md +142 -0
  93. package/src/skf-create-skill/{steps-c/step-05-compile.md → references/compile.md} +16 -16
  94. package/src/skf-create-skill/{steps-c/step-03d-component-extraction.md → references/component-extraction.md} +16 -16
  95. package/src/skf-create-skill/{steps-c/step-02-ecosystem-check.md → references/ecosystem-check.md} +4 -4
  96. package/src/skf-create-skill/{steps-c/step-04-enrich.md → references/enrich.md} +3 -3
  97. package/src/skf-create-skill/{steps-c/step-03-extract.md → references/extract.md} +52 -120
  98. package/src/skf-create-skill/references/extraction-patterns.md +8 -8
  99. package/src/skf-create-skill/{steps-c/step-07-generate-artifacts.md → references/generate-artifacts.md} +17 -9
  100. package/src/skf-create-skill/{steps-c/step-09-health-check.md → references/health-check.md} +4 -2
  101. package/src/skf-create-skill/{steps-c/step-01-load-brief.md → references/load-brief.md} +40 -32
  102. package/src/skf-create-skill/{steps-c/step-08-report.md → references/report.md} +8 -8
  103. package/src/skf-create-skill/references/source-resolution-protocols.md +6 -6
  104. package/src/skf-create-skill/{steps-c/sub/step-02b-ccc-discover.md → references/sub/ccc-discover.md} +5 -5
  105. package/src/skf-create-skill/{steps-c/sub/step-03c-fetch-docs.md → references/sub/fetch-docs.md} +28 -12
  106. package/src/skf-create-skill/{steps-c/sub/step-03b-fetch-temporal.md → references/sub/fetch-temporal.md} +81 -50
  107. package/src/skf-create-skill/{steps-c/step-06-validate.md → references/validate.md} +46 -26
  108. package/src/skf-create-stack-skill/SKILL.md +76 -17
  109. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +102 -0
  110. package/src/skf-create-stack-skill/assets/stack-skill-template.md +1 -1
  111. package/src/skf-create-stack-skill/customize.toml +46 -0
  112. package/src/skf-create-stack-skill/{steps-c/step-06-compile-stack.md → references/compile-stack.md} +6 -5
  113. package/src/skf-create-stack-skill/references/compose-mode-rules.md +3 -1
  114. package/src/skf-create-stack-skill/{steps-c/step-05-detect-integrations.md → references/detect-integrations.md} +32 -11
  115. package/src/skf-create-stack-skill/{steps-c/step-02-detect-manifests.md → references/detect-manifests.md} +28 -28
  116. package/src/skf-create-stack-skill/{steps-c/step-07-generate-output.md → references/generate-output.md} +11 -85
  117. package/src/skf-create-stack-skill/{steps-c/step-10-health-check.md → references/health-check.md} +4 -2
  118. package/src/skf-create-stack-skill/{steps-c/step-01-init.md → references/init.md} +70 -4
  119. package/src/skf-create-stack-skill/references/integration-patterns.md +2 -0
  120. package/src/skf-create-stack-skill/references/manifest-patterns.md +2 -0
  121. package/src/skf-create-stack-skill/{steps-c/step-04-parallel-extract.md → references/parallel-extract.md} +40 -17
  122. package/src/skf-create-stack-skill/{steps-c/step-03-rank-and-confirm.md → references/rank-and-confirm.md} +5 -4
  123. package/src/skf-create-stack-skill/{steps-c/step-09-report.md → references/report.md} +3 -3
  124. package/src/skf-create-stack-skill/{steps-c/step-08-validate.md → references/validate.md} +6 -6
  125. package/src/skf-drop-skill/SKILL.md +84 -12
  126. package/src/skf-drop-skill/customize.toml +57 -0
  127. package/src/skf-drop-skill/{steps-c/step-02-execute.md → references/execute.md} +49 -34
  128. package/src/skf-drop-skill/{steps-c/step-04-health-check.md → references/health-check.md} +6 -4
  129. package/src/skf-drop-skill/{steps-c/step-03-report.md → references/report.md} +13 -5
  130. package/src/skf-drop-skill/{steps-c/step-01-select.md → references/select.md} +63 -15
  131. package/src/skf-export-skill/SKILL.md +85 -16
  132. package/src/skf-export-skill/assets/managed-section-format.md +3 -3
  133. package/src/skf-export-skill/assets/snippet-format.md +2 -2
  134. package/src/skf-export-skill/customize.toml +49 -0
  135. package/src/skf-export-skill/{steps-c/step-03-generate-snippet.md → references/generate-snippet.md} +18 -12
  136. package/src/skf-export-skill/{steps-c/step-07-health-check.md → references/health-check.md} +6 -4
  137. package/src/skf-export-skill/{steps-c/step-01-load-skill.md → references/load-skill.md} +21 -51
  138. package/src/skf-export-skill/references/manifest-rebuild.md +68 -0
  139. package/src/skf-export-skill/references/multi-skill-mode.md +38 -0
  140. package/src/skf-export-skill/references/orphan-context-detection.md +75 -0
  141. package/src/skf-export-skill/references/orphan-row-detection.md +102 -0
  142. package/src/skf-export-skill/{steps-c/step-02-package.md → references/package.md} +7 -7
  143. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +74 -0
  144. package/src/skf-export-skill/{steps-c/step-06-summary.md → references/summary.md} +30 -6
  145. package/src/skf-export-skill/{steps-c/step-05-token-report.md → references/token-report.md} +5 -5
  146. package/src/skf-export-skill/{steps-c/step-04-update-context.md → references/update-context.md} +116 -91
  147. package/src/skf-quick-skill/SKILL.md +63 -123
  148. package/src/skf-quick-skill/customize.toml +44 -0
  149. package/src/skf-quick-skill/references/batch-mode.md +102 -0
  150. package/src/skf-quick-skill/{steps-c/step-04-compile.md → references/compile.md} +10 -10
  151. package/src/skf-quick-skill/{steps-c/step-02-ecosystem-check.md → references/ecosystem-check.md} +5 -5
  152. package/src/skf-quick-skill/{steps-c/step-06-finalize.md → references/finalize.md} +7 -7
  153. package/src/skf-quick-skill/{steps-c/step-07-health-check.md → references/health-check.md} +3 -3
  154. package/src/skf-quick-skill/{steps-c/step-03-quick-extract.md → references/quick-extract.md} +5 -5
  155. package/src/skf-quick-skill/references/registry-resolution.md +2 -0
  156. package/src/skf-quick-skill/{steps-c/step-01-resolve-target.md → references/resolve-target.md} +14 -10
  157. package/src/skf-quick-skill/{steps-c/step-05-write-and-validate.md → references/write-and-validate.md} +4 -4
  158. package/src/skf-refine-architecture/SKILL.md +86 -16
  159. package/src/skf-refine-architecture/customize.toml +49 -0
  160. package/src/skf-refine-architecture/{steps-c/step-05-compile.md → references/compile.md} +4 -4
  161. package/src/skf-refine-architecture/{steps-c/step-02-gap-analysis.md → references/gap-analysis.md} +5 -5
  162. package/src/skf-refine-architecture/{steps-c/step-07-health-check.md → references/health-check.md} +6 -4
  163. package/src/skf-refine-architecture/{steps-c/step-04-improvements.md → references/improvements.md} +5 -5
  164. package/src/skf-refine-architecture/references/init.md +144 -0
  165. package/src/skf-refine-architecture/{steps-c/step-03-issue-detection.md → references/issue-detection.md} +5 -5
  166. package/src/skf-refine-architecture/references/refinement-rules.md +2 -0
  167. package/src/skf-refine-architecture/{steps-c/step-06-report.md → references/report.md} +14 -5
  168. package/src/skf-rename-skill/SKILL.md +82 -12
  169. package/src/skf-rename-skill/customize.toml +52 -0
  170. package/src/skf-rename-skill/{steps-c/step-02-execute.md → references/execute.md} +94 -99
  171. package/src/skf-rename-skill/references/health-check.md +30 -0
  172. package/src/skf-rename-skill/references/rebuild-context.md +110 -0
  173. package/src/skf-rename-skill/{steps-c/step-03-report.md → references/report.md} +13 -5
  174. package/src/skf-rename-skill/{steps-c/step-01-select.md → references/select.md} +59 -20
  175. package/src/skf-setup/SKILL.md +35 -35
  176. package/src/skf-setup/customize.toml +33 -0
  177. package/src/skf-setup/{steps-c/step-03-auto-index.md → references/auto-index.md} +10 -18
  178. package/src/skf-setup/{steps-c/step-01b-ccc-index.md → references/ccc-index.md} +14 -22
  179. package/src/skf-setup/{steps-c/step-01-detect-and-tier.md → references/detect-and-tier.md} +23 -10
  180. package/src/skf-setup/{steps-c/step-05-health-check.md → references/health-check.md} +1 -1
  181. package/src/skf-setup/{steps-c/step-04-report.md → references/report.md} +21 -19
  182. package/src/skf-setup/references/tier-rules.md +1 -1
  183. package/src/skf-setup/{steps-c/step-02-write-config.md → references/write-config.md} +14 -13
  184. package/src/skf-test-skill/SKILL.md +77 -15
  185. package/src/skf-test-skill/customize.toml +54 -0
  186. package/src/skf-test-skill/{steps-c/step-04-coherence-check.md → references/coherence-check.md} +28 -71
  187. package/src/skf-test-skill/{steps-c/step-03-coverage-check.md → references/coverage-check.md} +16 -39
  188. package/src/skf-test-skill/{steps-c/step-02-detect-mode.md → references/detect-mode.md} +4 -26
  189. package/src/skf-test-skill/{steps-c/step-04b-external-validators.md → references/external-validators.md} +6 -28
  190. package/src/skf-test-skill/references/health-check.md +14 -0
  191. package/src/skf-test-skill/{steps-c/step-01-init.md → references/init.md} +21 -37
  192. package/src/skf-test-skill/references/migration-section-rules.md +4 -2
  193. package/src/skf-test-skill/{steps-c/step-06-report.md → references/report.md} +41 -51
  194. package/src/skf-test-skill/{steps-c/step-05-score.md → references/score.md} +26 -43
  195. package/src/skf-test-skill/references/scoring-rules.md +8 -6
  196. package/src/skf-test-skill/references/source-access-protocol.md +7 -5
  197. package/src/skf-test-skill/scripts/compute-score.py +88 -17
  198. package/src/skf-test-skill/templates/test-report-template.md +13 -13
  199. package/src/skf-update-skill/SKILL.md +23 -14
  200. package/src/skf-update-skill/customize.toml +44 -0
  201. package/src/skf-update-skill/{steps-c/step-02-detect-changes.md → references/detect-changes.md} +176 -68
  202. package/src/skf-update-skill/references/health-check.md +32 -0
  203. package/src/skf-update-skill/{steps-c/step-01-init.md → references/init.md} +56 -8
  204. package/src/skf-update-skill/references/manual-section-rules.md +4 -0
  205. package/src/skf-update-skill/references/merge-conflict-rules.md +4 -0
  206. package/src/skf-update-skill/{steps-c/step-04-merge.md → references/merge.md} +8 -8
  207. package/src/skf-update-skill/{steps-c/step-03-re-extract.md → references/re-extract.md} +53 -44
  208. package/src/skf-update-skill/references/remote-source-resolution.md +5 -1
  209. package/src/skf-update-skill/references/report.md +214 -0
  210. package/src/skf-update-skill/{steps-c/step-05-validate.md → references/validate.md} +5 -5
  211. package/src/skf-update-skill/{steps-c/step-06-write.md → references/write.md} +73 -51
  212. package/src/skf-verify-stack/SKILL.md +89 -17
  213. package/src/skf-verify-stack/assets/feasibility-report-template.md +4 -4
  214. package/src/skf-verify-stack/customize.toml +50 -0
  215. package/src/skf-verify-stack/references/coverage-patterns.md +2 -2
  216. package/src/skf-verify-stack/{steps-c/step-02-coverage.md → references/coverage.md} +7 -7
  217. package/src/skf-verify-stack/{steps-c/step-07-health-check.md → references/health-check.md} +7 -5
  218. package/src/skf-verify-stack/references/init.md +166 -0
  219. package/src/skf-verify-stack/references/integration-verification-rules.md +1 -1
  220. package/src/skf-verify-stack/{steps-c/step-03-integrations.md → references/integrations.md} +12 -12
  221. package/src/skf-verify-stack/{steps-c/step-06-report.md → references/report.md} +22 -41
  222. package/src/skf-verify-stack/{steps-c/step-04-requirements.md → references/requirements.md} +7 -7
  223. package/src/skf-verify-stack/{steps-c/step-05-synthesize.md → references/synthesize.md} +8 -8
  224. package/tools/validate-docs-drift.js +1 -1
  225. package/src/skf-audit-skill/steps-c/step-01-init.md +0 -221
  226. package/src/skf-refine-architecture/steps-c/step-01-init.md +0 -136
  227. package/src/skf-rename-skill/steps-c/step-04-health-check.md +0 -22
  228. package/src/skf-test-skill/steps-c/step-07-health-check.md +0 -25
  229. package/src/skf-update-skill/steps-c/step-07-report.md +0 -148
  230. package/src/skf-update-skill/steps-c/step-08-health-check.md +0 -22
  231. package/src/skf-verify-stack/steps-c/step-01-init.md +0 -178
@@ -1,5 +1,5 @@
1
1
  ---
2
- nextStepFile: './step-02-write-config.md'
2
+ nextStepFile: 'write-config.md'
3
3
  # Resolve `{mergeCccExclusionsHelper}` by probing `{mergeCccExclusionsProbeOrder}`
4
4
  # in order (installed SKF module path first, src/ dev-checkout fallback); first
5
5
  # existing path wins. HALT if neither resolves — the script owns config-value
@@ -16,7 +16,7 @@ mergeCccExclusionsProbeOrder:
16
16
 
17
17
  ## STEP GOAL:
18
18
 
19
- If ccc is available (`{ccc: true}` from step-01), invoke `{mergeCccExclusionsHelper}` to validate config-driven SKF exclusion patterns and merge them into `.cocoindex_code/settings.yml`, then verify the project's ccc index exists and create or refresh it if needed. Store index state and exclusion-merge results in context for step-02 to write into forge-tier.yaml and for step-04 to surface in the JSON envelope.
19
+ If ccc is available (`{ccc: true}` from step 1), invoke `{mergeCccExclusionsHelper}` to validate config-driven SKF exclusion patterns and merge them into `.cocoindex_code/settings.yml`, then verify the project's ccc index exists and create or refresh it if needed. Store index state and exclusion-merge results in context for step 2 to write into forge-tier.yaml and for step 4 to surface in the JSON envelope.
20
20
 
21
21
  For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed.
22
22
 
@@ -31,36 +31,28 @@ For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed
31
31
 
32
32
  ### 1. Check Eligibility
33
33
 
34
- Read `{ccc}` from step-01 context.
34
+ Read `{ccc}` and `{ccc_skip_index}` from context.
35
35
 
36
36
  **If `{ccc}` is false:** Set `{ccc_index_result: "none", ccc_indexed_path: null, ccc_last_indexed: null, ccc_exclude_patterns: [], ccc_exclusion_warnings: [], settings_yml_written: false, settings_yml_patterns_added: 0}`. Proceed directly to section 4 (Auto-Proceed) — no output, no messaging.
37
37
 
38
- **If `{ccc}` is true:** Continue to section 2.
38
+ **If `{ccc}` is true AND `{ccc_skip_index}` is true:** Run the exclusion-merge (section 3) so settings.yml stays current, then set `{ccc_index_result: "skipped", ccc_indexed_path: null, ccc_last_indexed: null}` and proceed to section 4 — do NOT run `ccc init` or `ccc index`. The envelope's `ccc_index.status` will be `"skipped"` so pipelines that plan an out-of-band re-index can distinguish "the operator opted out" from "indexing failed".
39
39
 
40
- ### 2. Check Existing Index State
41
-
42
- Read existing forge-tier.yaml at `{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml` (if it exists from a previous run).
40
+ **If `{ccc}` is true AND `{ccc_skip_index}` is false:** Continue to section 2.
43
41
 
44
- Read `staleness_threshold_hours` from `ccc_index.staleness_threshold_hours` in the existing forge-tier.yaml (default: 24 if not present or not a number). Use this value for the freshness check below.
45
-
46
- Check the `ccc_index` section:
42
+ ### 2. Check Existing Index State
47
43
 
48
- - If `ccc_index.indexed_path` matches `{project-root}` AND `ccc_index.status` is `"fresh"` or `"created"`:
49
- - Check freshness: if `ccc_index.last_indexed` is within `staleness_threshold_hours` of now → index is fresh
50
- - Store `{ccc_index_result: "fresh", ccc_indexed_path: {project-root}, ccc_last_indexed: {existing timestamp}}`
51
- - Set `{needs_reindex: false}` — exclusions still get merged in section 3, which may force a re-index
44
+ Consume prior CCC state from stage 1's detector output (`prior.previous_ccc_*` context flags) — no YAML re-parse here, the detector already read forge-tier.yaml. Use `prior.previous_ccc_staleness_threshold_hours` if set, else default 24 hours.
52
45
 
53
- - If `ccc_index.indexed_path` matches `{project-root}` but timestamp is older than threshold:
54
- - Set `{needs_reindex: true}`
46
+ Decide `{needs_reindex}` and `{ccc_index_result}` from those flags:
55
47
 
56
- - If `ccc_index` is missing, has null values, or path doesn't match:
57
- - Set `{needs_reindex: true}`
48
+ - If `previous_ccc_indexed_path` matches `{project-root}` AND `previous_ccc_index_status` is `"fresh"` or `"created"` AND `previous_ccc_last_indexed` is within `staleness_threshold_hours` of now → index is fresh. Set `{needs_reindex: false}`, `{ccc_index_result: "fresh", ccc_indexed_path: {project-root}, ccc_last_indexed: <existing timestamp>}`. Exclusions still merge in section 3 (which may force a re-index).
49
+ - If path matches but the timestamp is older than threshold, or if the path doesn't match, or if any prior CCC field is null → `{needs_reindex: true}`.
58
50
 
59
51
  ### 3. Merge SKF Exclusion Patterns
60
52
 
61
53
  SKF infrastructure and output directories must be excluded from the CCC index — they contain workflow instructions, build artifacts, and generated skills that pollute semantic search results with zero extraction value.
62
54
 
63
- Run the merge helper, forwarding the resolved config values from the workflow activation context. Invoke via `uv run` so PEP 723 inline metadata resolves the script's PyYAML dependency automatically (per `docs/getting-started.md`'s prereq list — uv exists for this exact purpose). Bare `python3` will fail on a fresh Python with `ModuleNotFoundError: No module named 'yaml'`.
55
+ Forward `skills_output_folder` and `forge_data_folder` from `{project-root}/_bmad/skf/config.yaml` **verbatim** — the script resolves `{project-root}/...` template strings and rejects absolute paths / placeholders / glob meta-chars internally. The step does no string surgery; that work moved into the helper. Invoke via `uv run` so PEP 723 inline metadata resolves the script's PyYAML dependency automatically (per `docs/getting-started.md`'s prereq list — uv exists for this exact purpose). Bare `python3` will fail on a fresh Python with `ModuleNotFoundError: No module named 'yaml'`.
64
56
 
65
57
  ```bash
66
58
  uv run {mergeCccExclusionsHelper} \
@@ -76,8 +68,8 @@ The script (see `src/shared/scripts/skf-merge-ccc-exclusions.py` docstring for t
76
68
  - `{settings_yml_existed}` ← `settings_yml_existed`
77
69
  - `{settings_yml_written}` ← `written`
78
70
  - `{settings_yml_patterns_added}` ← `patterns_added`
79
- - `{ccc_exclude_patterns}` ← derive from the script's behaviour: it always merges the 4 always-include patterns plus any of the 2 config-driven ones whose value passed validation. Step-02 needs this list to write `ccc_index.exclude_patterns` into forge-tier.yaml read it from the script's `patterns_added_list` PLUS any already-present matching SKF patterns inferred from the input. Pragmatically: re-derive the list as `["**/_bmad", "**/_bmad-output", "**/.claude", "**/_skf-learn"]` plus `"**/{skills_output_folder}"` if no warning names `skills_output_folder` plus `"**/{forge_data_folder}"` if no warning names `forge_data_folder`.
80
- - `{ccc_exclusion_warnings}` ← `warnings` (a list — step-04 folds them into the envelope's warnings array)
71
+ - `{ccc_exclude_patterns}` ← `effective_patterns` (the script returns the final, sorted, deduplicated SKF pattern set after validation — consume verbatim; do NOT re-derive in prose).
72
+ - `{ccc_exclusion_warnings}` ← `warnings` (a list — step 4 folds them into the envelope's warnings array)
81
73
 
82
74
  **If `{settings_yml_written}` is true** (new patterns merged into settings.yml): set `{needs_reindex: true}` — new exclusions require re-indexing for the index to reflect them. Display: "**CCC exclusions configured:** {patterns_added} SKF patterns applied to .cocoindex_code/settings.yml"
83
75
 
@@ -120,7 +112,7 @@ ccc index
120
112
 
121
113
  **If fails:**
122
114
 
123
- - Store `{ccc_index_result: "failed", ccc_indexed_path: null, ccc_last_indexed: null, ccc_indexing_failed_reason: {error}}` (the failed-reason flag flows into step-04's envelope warnings)
115
+ - Store `{ccc_index_result: "failed", ccc_indexed_path: null, ccc_last_indexed: null, ccc_indexing_failed_reason: {error}}` (the failed-reason flag flows into step 4's envelope warnings)
124
116
  - Display: "CCC indexing failed: {error}. Extraction will use direct AST scanning — semantic pre-ranking unavailable this session."
125
117
  - Continue — this is NOT a workflow error
126
118
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- nextStepFile: './step-01b-ccc-index.md'
2
+ nextStepFile: 'ccc-index.md'
3
3
  # Resolve `{detectToolsHelper}` by probing `{detectToolsProbeOrder}` in order
4
4
  # (installed SKF module path first, src/ dev-checkout fallback); first existing
5
5
  # path wins. HALT if neither resolves — the script is the source of truth for
@@ -27,17 +27,14 @@ Verify availability of the four forge tools (ast-grep, gh, qmd, ccc), read any e
27
27
 
28
28
  ### 1. Check for Existing Configuration (Re-run Detection)
29
29
 
30
- **Read existing forge-tier.yaml** at `{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml`:
31
-
32
- - If exists: store the current `tier` value as `{previous_tier}`, `tier_detected_at` as `{previous_detection_date}`, and the `tools` map as `{previous_tools}` (for the tool-set delta detection step-04 needs to surface newly-installed tools on same-tier re-runs).
33
- - If not found: set `{previous_tier}` to null and `{previous_tools}` to an empty map (first run).
30
+ Prior state lives in two files; the detector helper (§2) reads forge-tier.yaml itself when invoked with `--prior-state-from`, so this section only handles preferences.yaml directly and the first-run preamble.
34
31
 
35
32
  **Read existing preferences.yaml** at `{project-root}/_bmad/_memory/forger-sidecar/preferences.yaml`:
36
33
 
37
34
  - If exists: check for `tier_override` value
38
35
  - If not found: set `{tier_override}` to null
39
36
 
40
- **First-run preamble** — when `{previous_tier}` is null AND `{headless_mode}` is `false`, display this preamble before continuing so the user knows what is about to happen and can abort cleanly with Esc / Ctrl+C before any writes:
37
+ **First-run preamble** — when the prior-state read (§2 below, `prior.previous_tier`) returns null AND `{headless_mode}` is `false`, display this preamble before continuing so the user knows what is about to happen and can abort cleanly with Esc / Ctrl+C before any writes. (When `{headless_mode}` is true, skip the preamble entirely — pipelines never need it.)
41
38
 
42
39
  "**About to set up the forge.** This workflow will:
43
40
 
@@ -53,7 +50,7 @@ Press Esc or Ctrl+C now if this isn't the right project — no files have been w
53
50
 
54
51
  ### 2. Run Detection Helper
55
52
 
56
- Build the Bash invocation. Start with `uv run {detectToolsHelper}` `uv run` honors the script's PEP 723 inline-metadata dependency declarations (resolved automatically on first call; cached thereafter), which is why uv is documented as a runtime prerequisite in `docs/getting-started.md`. Do NOT call the script via bare `python3` — `python3` ignores PEP 723 metadata, and on a fresh interpreter without the declared deps installed system-wide the call fails with `ModuleNotFoundError`. If `{tier_override}` is non-null, append `--tier-override "{tier_override}"` (the script handles invalid values by flagging them — do NOT pre-validate). If `{require_tier}` (resolved in On Activation) is non-null, append `--require-tier "{require_tier}"`. Then execute.
53
+ Build the Bash invocation: `uv run {detectToolsHelper} --prior-state-from "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml"`. If `{tier_override}` is non-null, append `--tier-override "{tier_override}"`. If `{require_tier}` is non-null, append `--require-tier "{require_tier}"`. Then execute. (The script's PEP 723 metadata is why `uv run` is required — bare `python3` skips dependency resolution.)
57
54
 
58
55
  The script (see `src/shared/scripts/skf-detect-tools.py` docstring for the full `DETECT_OUTPUT_SCHEMA`) probes ast-grep / gh / qmd / ccc concurrently with two-step verification for qmd and ccc (binary-identity check + daemon-health check, including the `CocoIndex Code` identity-marker substring check that rejects PATH-shadowing aliases). It applies the 4-rule tier table, performs the tier-override sanity check (override is honored but flagged unsafe when underlying tools are missing), and evaluates `--require-tier` using a tool-prerequisite check (Deep does NOT subsume Forge+ — Deep does not require ccc). Output is one JSON document on stdout.
59
56
 
@@ -68,7 +65,7 @@ From `tools`:
68
65
  - `{gh_cli}` ← `tools.gh_cli.available`
69
66
  - `{gh_cli_version}` ← `tools.gh_cli.version`
70
67
  - `{qmd}` ← `tools.qmd.available`
71
- - `{qmd_status}` ← `tools.qmd.status` (`"absent" | "daemon_stopped" | "healthy"` — drives the climb-hint distinction in step-04)
68
+ - `{qmd_status}` ← `tools.qmd.status` (`"absent" | "daemon_stopped" | "healthy"` — drives the climb-hint distinction in step 4)
72
69
  - `{qmd_version}` ← `tools.qmd.version`
73
70
  - `{ccc}` ← `tools.ccc.available`
74
71
  - `{ccc_daemon}` ← `tools.ccc.daemon` (`"healthy" | "stopped" | "error" | null`)
@@ -82,15 +79,31 @@ From `tier`:
82
79
  - `{tier_override_active}` ← `tier.override_applied`
83
80
  - `{tier_override_invalid}` ← `tier.override_invalid`
84
81
  - `{tier_override_invalid_value}` ← `tier.override_invalid_value`
85
- - `{tier_override_invalid_suggestion}` ← `tier.override_invalid_suggestion` (closest valid tier name from a fuzzy match — `null` when no candidate cleared the cutoff or when override is valid; consumed by step-04's invalid-override note as a "did you mean ...?" hint)
82
+ - `{tier_override_invalid_suggestion}` ← `tier.override_invalid_suggestion` (closest valid tier name from a fuzzy match — `null` when no candidate cleared the cutoff or when override is valid; consumed by step 4's invalid-override note as a "did you mean ...?" hint)
86
83
  - `{tier_override_unsafe}` ← `tier.override_unsafe`
87
- - `{tier_override_unsafe_missing}` ← `tier.override_unsafe_missing` (a list — step-04 joins with `", "` for display)
84
+ - `{tier_override_unsafe_missing}` ← `tier.override_unsafe_missing` (a list — step 4 joins with `", "` for display)
88
85
 
89
86
  From `require_tier`:
90
87
 
91
88
  - `{require_tier_satisfied}` ← `require_tier.satisfied` (`true | false | null`; null when `--require-tier` was not set)
92
89
  - `{require_tier_failure_missing_tools}` ← `require_tier.missing_tools` (a list)
93
90
 
91
+ From `prior` (populated by `--prior-state-from`; all fields null/empty on first run):
92
+
93
+ - `{previous_tier}` ← `prior.previous_tier`
94
+ - `{previous_detection_date}` ← `prior.previous_detection_date`
95
+ - `{previous_tools}` ← `prior.previous_tools`
96
+ - `{previous_ccc_index_status}` ← `prior.previous_ccc_index_status`
97
+ - `{previous_ccc_indexed_path}` ← `prior.previous_ccc_indexed_path`
98
+ - `{previous_ccc_last_indexed}` ← `prior.previous_ccc_last_indexed`
99
+ - `{previous_ccc_staleness_threshold_hours}` ← `prior.previous_ccc_staleness_threshold_hours`
100
+
101
+ From `deltas` (computed by the script from current tools + prior; eliminates LLM-side set arithmetic in the report banner):
102
+
103
+ - `{tools_added}` ← `deltas.tools_added` (the list)
104
+ - `{tools_removed}` ← `deltas.tools_removed` (the list)
105
+ - `{tier_changed}` ← `deltas.tier_changed` (boolean)
106
+
94
107
  **The script is the source of truth.** Every tier-rules edge case (override-honored-but-unsafe, Deep-doesn't-subsume-Forge+, qmd-binary-vs-daemon distinction, ccc-identity-marker rejection of foreign binaries) is locked into the test suite at `test/test-skf-detect-tools.py`. Substituting prose-driven logic for the script's output here will cause subtle re-run regressions that the prompt's prose is no longer detailed enough to catch.
95
108
 
96
109
  ### 4. Auto-Proceed
@@ -15,7 +15,7 @@ Chain to the shared workflow self-improvement health check at `{nextStepFile}`.
15
15
 
16
16
  ## Rules
17
17
 
18
- - No user-facing reports, file writes, or result contracts in this step — those belong in step-04
18
+ - No user-facing reports, file writes, or result contracts in this step — those belong in step 4
19
19
  - Delegate directly to `{nextStepFile}` with no additional commentary
20
20
  - Do not attempt any other action between loading this step and executing `{nextStepFile}`
21
21
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  tierRulesData: 'references/tier-rules.md'
3
- nextStepFile: './step-05-health-check.md'
3
+ nextStepFile: 'health-check.md'
4
4
  # Resolve `{emitEnvelopeHelper}` by probing `{emitEnvelopeProbeOrder}` in order
5
5
  # (installed SKF module path first, src/ dev-checkout fallback); first existing
6
6
  # path wins. HALT if neither resolves when the headless envelope must be emitted
@@ -31,9 +31,9 @@ Display the forge status report with positive capability framing, surface tier c
31
31
 
32
32
  ## Headless Mode Display Rule
33
33
 
34
- When `{headless_mode}` is `true`, sections 2 (the human-readable banner) and 3 (the REQUIRED TIER NOT MET human prose block) are **skipped entirely**. The single line of stdout in headless mode is the `SKF_SETUP_RESULT_JSON: {…}` envelope from section 4 — every signal the human banner would have surfaced is already in the envelope (`tier`, `previous_tier`, `tier_changed`, `tools`, `tools_added`, `tools_removed`, `files_written`, `tier_override_*`, `require_tier_satisfied`, `warnings`, `error`). Parent skills and CI pipelines consume one parseable line; they should not be forced to scroll past 30 lines of ASCII-art they cannot use.
34
+ When `{headless_mode}` is `true` OR `{quiet_mode}` is `true`, sections 2 (the human-readable banner) and 3 (the REQUIRED TIER NOT MET human prose block) are **skipped entirely**. The single line of stdout in those modes is the `SKF_SETUP_RESULT_JSON: {…}` envelope from section 4 — every signal the human banner would have surfaced is already in the envelope (`tier`, `previous_tier`, `tier_changed`, `tools`, `tools_added`, `tools_removed`, `files_written`, `tier_override_*`, `require_tier_satisfied`, `warnings`, `error`). Parent skills, CI pipelines, and expert re-runners consume the envelope alone; they should not be forced to scroll past 30 lines of ASCII-art they cannot use.
35
35
 
36
- When `{headless_mode}` is `false`, sections 2 and 3 display normally and section 4 is skipped (interactive runs read the human banner; envelope emission would just be log noise).
36
+ When both `{headless_mode}` and `{quiet_mode}` are `false`, sections 2 and 3 display normally and section 4 is skipped (interactive runs read the human banner; envelope emission would just be log noise).
37
37
 
38
38
  ## MANDATORY SEQUENCE
39
39
 
@@ -41,7 +41,7 @@ When `{headless_mode}` is `false`, sections 2 and 3 display normally and section
41
41
 
42
42
  Load and read {tierRulesData} for the tier capability descriptions and re-run messages. Needed by section 2; safe to load unconditionally — the data load itself is silent.
43
43
 
44
- ### 2. Display Forge Status Report (skip when `{headless_mode}` is true)
44
+ ### 2. Display Forge Status Report (skip when `{headless_mode}` or `{quiet_mode}` is true)
45
45
 
46
46
  **Format the report as follows:**
47
47
 
@@ -80,7 +80,11 @@ Load and read {tierRulesData} for the tier capability descriptions and re-run me
80
80
  {end if}
81
81
 
82
82
  {if hygiene_result is "completed" and hygiene_healthy is 0:}
83
- QMD Registry: empty — collections are created automatically when you run [CS] Create Skill.
83
+ QMD Registry: empty — collections are created automatically when you run /skf-create-skill.
84
+ {end if}
85
+
86
+ {if hygiene_result is "qmd_unavailable":}
87
+ QMD Registry: skipped (daemon error — retry after `qmd start`).
84
88
  {end if}
85
89
 
86
90
  {if tools.ccc is true:}
@@ -95,9 +99,7 @@ Load and read {tierRulesData} for the tier capability descriptions and re-run me
95
99
  {if preferences_yaml_created is true:}
96
100
  - preferences.yaml — {project-root}/_bmad/_memory/forger-sidecar/preferences.yaml (first-run defaults)
97
101
  {end if}
98
- {if forge_data_dir_created is true:}
99
- - {forge_data_folder}/ (directory created)
100
- {end if}
102
+ - {forge_data_folder}/ (directory ensured)
101
103
  {if settings_yml_written is true:}
102
104
  - .cocoindex_code/settings.yml — {project-root}/.cocoindex_code/settings.yml ({settings_yml_patterns_added} SKF exclusion pattern(s) merged)
103
105
  {end if}
@@ -119,13 +121,13 @@ Load and read {tierRulesData} for the tier capability descriptions and re-run me
119
121
  rely on the missing tool(s) will fail at runtime. Install the missing tool(s) or remove
120
122
  the override from preferences.yaml.
121
123
 
122
- {if re-run with tier change:}
124
+ {if {tier_changed} is true:}
123
125
  {appropriate upgrade/downgrade message from tier-rules.md}
124
126
 
125
- {if re-run with same tier and {tools_added} is empty and {tools_removed} is empty:}
127
+ {if {tier_changed} is false and {tools_added} is empty and {tools_removed} is empty and {previous_tier} is non-null:}
126
128
  {same-tier message from tier-rules.md}
127
129
 
128
- {if re-run with same tier and ({tools_added} or {tools_removed} is non-empty):}
130
+ {if {tier_changed} is false and ({tools_added} or {tools_removed} is non-empty):}
129
131
  Tier unchanged: {calculated_tier}.
130
132
  {if {tools_added} non-empty:} Newly detected: {comma-separated tool names from tools_added}{if ccc was added and tier is Deep: " — ccc enhances Deep tier transparently."}
131
133
  {if {tools_removed} non-empty:} No longer detected: {comma-separated tool names from tools_removed} — re-install to restore those capabilities.
@@ -135,10 +137,10 @@ Load and read {tierRulesData} for the tier capability descriptions and re-run me
135
137
  ═══════════════════════════════════════
136
138
 
137
139
  {if {headless_mode} is false:}
138
- Next: try [BS] Brief Skill to scope your first compilation target, or [QS] Quick Skill for a fast template-driven path. Already have a skill? [AS] Audit Skill drift-checks an existing skill against current sources.
140
+ Next: try `/skf-brief-skill` to scope your first compilation target, or `/skf-quick-skill` for a fast template-driven path. Already have a skill? `/skf-audit-skill` drift-checks an existing skill against current sources.
139
141
  ```
140
142
 
141
- The same-tier-with-tool-deltas branch reads `{tools_added}` and `{tools_removed}` directly from step-01's context (set from `skf-detect-tools.py` output relative to `{previous_tools}`). No LLM-side recomputation.
143
+ All re-run-delta context flags (`{tools_added}`, `{tools_removed}`, `{tier_changed}`) come from the detector's `deltas` block bound in stage 1 no LLM-side recomputation, no set arithmetic in prose.
142
144
 
143
145
  **Tool display rules:**
144
146
 
@@ -146,9 +148,9 @@ The same-tier-with-tool-deltas branch reads `{tools_added}` and `{tools_removed}
146
148
  - Do NOT list unavailable tools
147
149
  - Do NOT show a "missing" column or section
148
150
 
149
- ### 3. Display Required-Tier Failure Block (when applicable; skip when `{headless_mode}` is true)
151
+ ### 3. Display Required-Tier Failure Block (when applicable; skip when `{headless_mode}` or `{quiet_mode}` is true)
150
152
 
151
- If `{require_tier_satisfied}` is `false` AND `{headless_mode}` is `false`, display this block immediately after the status report. In headless mode the failure surfaces via the envelope's `require_tier_satisfied: false` and the synthesized `warnings` entry — the human prose block would be log noise that pipelines have to scroll past.
153
+ If `{require_tier_satisfied}` is `false` AND both `{headless_mode}` and `{quiet_mode}` are `false`, display this block immediately after the status report. In headless or quiet modes the failure surfaces via the envelope's `require_tier_satisfied: false` and the synthesized `warnings` entry — the human prose block would be log noise.
152
154
 
153
155
  When the block does fire (interactive run with require-tier failure):
154
156
 
@@ -170,7 +172,7 @@ This block exists to make pipeline failures visible without the operator parsing
170
172
 
171
173
  ### 4. Emit Headless JSON Envelope
172
174
 
173
- When `{headless_mode}` is `true`, build the context payload from this step's accumulated flags and forward it to `{emitEnvelopeHelper}` on stdin. Invoke via `uv run` so the script's PEP 723 dependency declarations are honored (the canonical runtime invocation per `docs/getting-started.md`'s uv prereq — bare `python3` skips the metadata block and breaks on a fresh interpreter). The script computes derived fields (`tools_added`, `tools_removed`, `tier_changed`, `warnings`), validates the assembled envelope against the JSON Schema at `src/shared/scripts/schemas/skf-setup-result-envelope.v1.json`, and emits the single prefixed line `SKF_SETUP_RESULT_JSON: {…}` on stdout.
175
+ When `{headless_mode}` is `true` OR `{quiet_mode}` is `true`, build the context payload from this step's accumulated flags and forward it to `{emitEnvelopeHelper}` on stdin. Invoke via `uv run` so the script's PEP 723 dependency declarations are honored (the canonical runtime invocation per `docs/getting-started.md`'s uv prereq — bare `python3` skips the metadata block and breaks on a fresh interpreter). The script computes derived fields (`tools_added`, `tools_removed`, `tier_changed`, `warnings`), validates the assembled envelope against the JSON Schema at `src/shared/scripts/schemas/skf-setup-result-envelope.v1.json`, and emits the single prefixed line `SKF_SETUP_RESULT_JSON: {…}` on stdout.
174
176
 
175
177
  ```bash
176
178
  echo '{
@@ -205,12 +207,12 @@ echo '{
205
207
  }' | uv run {emitEnvelopeHelper} emit
206
208
  ```
207
209
 
208
- The script's documented context-payload shape (see `src/shared/scripts/skf-emit-result-envelope.py` docstring) tolerates two `tools` shapes — bare booleans OR `skf-detect-tools.py`'s `{key: {available: bool, ...}}` output — so either step-01's normalized booleans OR the raw detect-tools output forwarded as-is will produce the correct envelope.
210
+ The script's documented context-payload shape (see `src/shared/scripts/skf-emit-result-envelope.py` docstring) tolerates two `tools` shapes — bare booleans OR `skf-detect-tools.py`'s `{key: {available: bool, ...}}` output — so either step 1's normalized booleans OR the raw detect-tools output forwarded as-is will produce the correct envelope.
209
211
 
210
212
  **If the script exits non-zero:** the assembled envelope failed schema validation, which means a context flag from an earlier step is malformed. Surface the error to stderr and continue (the human-readable banner is already displayed; missing JSON envelope on a headless run is a degraded but non-fatal state — the pipeline observer will see no envelope and treat that as "agent did not complete cleanly").
211
213
 
212
- **When `{headless_mode}` is `false`, do NOT invoke the script** — interactive runs read the human-readable banner.
214
+ **When both `{headless_mode}` and `{quiet_mode}` are `false`, do NOT invoke the script** — interactive runs read the human-readable banner.
213
215
 
214
216
  ### 5. Chain to Health Check
215
217
 
216
- After the forge status report (and any failure block + JSON envelope) has been displayed, load `{nextStepFile}`, read it fully, and execute it — UNLESS `{require_tier_satisfied}` is `false`, in which case halt the workflow here without chaining to step-05. The health-check step is the true terminal step on success — do not stop after the report on a passing run even though it reads as final. step-05 in turn delegates to `shared/health-check.md`; after that returns, the setup workflow is fully done.
218
+ After the forge status report (and any failure block + JSON envelope) has been displayed, load `{nextStepFile}`, read it fully, and execute it — UNLESS `{require_tier_satisfied}` is `false`, in which case halt the workflow here without chaining to step 5. The health-check step is the true terminal step on success — do not stop after the report on a passing run even though it reads as final. step 5 in turn delegates to `shared/health-check.md`; after that returns, the setup workflow is fully done.
@@ -7,7 +7,7 @@
7
7
  | ast-grep | `ast-grep --version` | Returns version string without error |
8
8
  | gh | `gh --version` | Returns version string without error |
9
9
  | qmd | `qmd status` | Returns status indicating initialized and operational |
10
- | ccc | Step A: `ccc --help` Step B: `ccc doctor` | Step A: exits 0 AND help output identifies the binary as cocoindex-code. Step B: daemon healthy. See `steps-c/step-01-detect-and-tier.md` §7 for the full identity-marker procedure. |
10
+ | ccc | Step A: `ccc --help` Step B: `ccc doctor` | Step A: exits 0 AND help output identifies the binary as cocoindex-code. Step B: daemon healthy. See `references/detect-and-tier.md` §7 for the full identity-marker procedure. |
11
11
 
12
12
  **Important:** Use verification commands, not existence checks (`which`, `command -v`). A tool must be functional, not just present on PATH. For daemon-based tools (ccc), verify both binary identity and daemon health — a binary with the right name but the wrong implementation is a false positive, not a tool.
13
13
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- nextStepFile: './step-03-auto-index.md'
2
+ nextStepFile: 'auto-index.md'
3
3
  # Resolve `{forgeTierRwHelper}` by probing `{forgeTierRwProbeOrder}` in order
4
4
  # (installed SKF module path first, src/ dev-checkout fallback); first existing
5
5
  # path wins. HALT if neither resolves — the script owns the canonical
@@ -10,6 +10,12 @@ nextStepFile: './step-03-auto-index.md'
10
10
  forgeTierRwProbeOrder:
11
11
  - '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
12
12
  - '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
13
+ # Resolve `{emitEnvelopeHelper}` for the headless/quiet blocked-envelope emit
14
+ # on write failures (the regular envelope assembly happens in step 4, but
15
+ # write halts never reach step 4 — see section 1's blocked-emit protocol).
16
+ emitEnvelopeProbeOrder:
17
+ - '{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py'
18
+ - '{project-root}/src/shared/scripts/skf-emit-result-envelope.py'
13
19
  ---
14
20
 
15
21
  <!-- Config: communicate in {communication_language}. Halt-on-write-failure messages render in the user's language. -->
@@ -23,7 +29,7 @@ Write the detected tool availability and calculated tier to `forge-tier.yaml` (p
23
29
  ## Rules
24
30
 
25
31
  - Focus only on writing configuration files and creating directories
26
- - Do not re-detect tools — use results from step-01
32
+ - Do not re-detect tools — use results from step 1
27
33
  - Never inline a YAML template for forge-tier.yaml or preferences.yaml — the script owns the canonical format
28
34
  - File write failures are errors — report clearly and halt the workflow
29
35
 
@@ -31,7 +37,7 @@ Write the detected tool availability and calculated tier to `forge-tier.yaml` (p
31
37
 
32
38
  ### 1. Write forge-tier.yaml
33
39
 
34
- Build the JSON payload from context flags set by step-01 and step-01b. The payload must include `tools`, `tier`, and `ccc_index`; the script handles `tier_detected_at` defaulting to "now" if absent and preserves `qmd_collections`, `ccc_index_registry`, and a user-customized `ccc_index.staleness_threshold_hours` from any existing file.
40
+ Build the JSON payload from context flags set by step 1 and step 1b. The payload must include `tools`, `tier`, and `ccc_index`; the script handles `tier_detected_at` defaulting to "now" if absent and preserves `qmd_collections`, `ccc_index_registry`, and a user-customized `ccc_index.staleness_threshold_hours` from any existing file.
35
41
 
36
42
  Invoke via `uv run` so the script's PEP 723 PyYAML dependency resolves automatically (this is what `docs/getting-started.md`'s uv prereq exists for). Bare `python3` would fail on a fresh interpreter with `ModuleNotFoundError`.
37
43
 
@@ -59,13 +65,13 @@ echo '{
59
65
 
60
66
  The script atomically writes the file via temp + fsync + rename (mirrors `skf-atomic-write.py`'s crash-safety contract) and returns a JSON response with `wrote`, `preserved_arrays.qmd_collections` count, `preserved_arrays.ccc_index_registry` count, and the resolved `tier`.
61
67
 
62
- **Parse the response and set context flags for step-04:**
68
+ **Parse the response and set context flags for step 4:**
63
69
 
64
70
  - `{forge_tier_yaml_path}` ← `wrote`
65
71
  - `{forge_tier_qmd_collections_count}` ← `preserved_arrays.qmd_collections`
66
72
  - `{forge_tier_ccc_registry_count}` ← `preserved_arrays.ccc_index_registry`
67
73
 
68
- **If the script exits non-zero**: parse the stderr JSON `{"status":"error","message":...}`, set `{error: {phase: "step-02:write-tools", path: "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml", reason: <message>}}` for step-04's envelope, and halt the workflow before chaining to step-03.
74
+ **If the script exits non-zero**: parse the stderr JSON `{"status":"error","message":...}`, set `{error: {phase: "step 2:write-tools", path: "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml", reason: <message>}}`, and halt the workflow before chaining to step 3. When `{headless_mode}` or `{quiet_mode}` is true, first pipe the same `{phase, reason, path}` payload to `python3 {emitEnvelopeHelper} emit-blocked` so the pipeline sees the blocked envelope before the halt (see `references/report.md` frontmatter for the helper probe order; the subcommand declares zero dependencies).
69
75
 
70
76
  ### 2. Initialize preferences.yaml
71
77
 
@@ -76,20 +82,15 @@ uv run {forgeTierRwHelper} init-prefs \
76
82
 
77
83
  The script creates the file with first-run defaults (matching the prior inline template — `tier_override: ~`, `passive_context: true`, `headless_mode: false`, `compact_greeting: false`, plus reserved-for-future-use commented fields) IF the file does not exist. When the file already exists, the script refuses to overwrite (preserves user customization) and reports `wrote: false`.
78
84
 
79
- **Parse the response and set context flags for step-04:**
85
+ **Parse the response and set context flags for step 4:**
80
86
 
81
87
  - `{preferences_yaml_created}` ← `wrote` (true on first run, false on re-run when the file pre-existed)
82
88
 
83
- **If the script exits non-zero**: same halt-and-report pattern as section 1, with `phase: "step-02:init-prefs"` and the matching path.
89
+ **If the script exits non-zero**: same halt-and-blocked-envelope-emit pattern as section 1, with `phase: "step 2:init-prefs"` and the matching path.
84
90
 
85
91
  ### 3. Ensure forge-data/ Directory
86
92
 
87
- Check if `{forge_data_folder}` directory exists:
88
-
89
- - If missing: create it. Store `{forge_data_dir_created: true}` in context.
90
- - If exists: skip silently. Store `{forge_data_dir_created: false}` in context.
91
-
92
- If the create fails (parent unwritable, disk full, permission denied), set `{error: {phase: "step-02:forge-data-dir", path: "{forge_data_folder}", reason: <error message>}}` for step-04's envelope and halt the workflow.
93
+ Run `mkdir -p {forge_data_folder}`. The `-p` flag is idempotent (creates parents, exits 0 if the directory already exists), so the prompt does no existence-check reasoning. On non-zero exit, halt with the same blocked-envelope-emit pattern as section 1, using `phase: "step 2:forge-data-dir"` and the matching path.
93
94
 
94
95
  ### 4. Auto-Proceed
95
96
 
@@ -9,6 +9,15 @@ description: Cognitive completeness verification — quality gate before export.
9
9
 
10
10
  Verifies that a skill is complete enough to be useful to an AI agent by checking coverage of the public API surface (naive mode) or validating SKILL.md + references coherence (contextual mode). Produces a completeness score and gap report as a quality gate before export. Every finding must trace to actual code with file:line citations.
11
11
 
12
+ ## Conventions
13
+
14
+ - Bare paths (e.g. `references/<name>.md`) resolve from the skill root.
15
+ - `references/` holds prompt content carved out of SKILL.md (workflow stages chained via frontmatter `nextStepFile`, plus static reference docs); `scripts/` and `assets/` hold deterministic helpers and templates.
16
+ - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).
17
+ - `{project-root}`-prefixed paths resolve from the project working directory.
18
+ - `{skill-name}` resolves to the skill directory's basename.
19
+ - Step files use `## STEP GOAL:` headings rather than the `## MANDATORY SEQUENCE` + `## CRITICAL STEP COMPLETION NOTE` pattern used by generate-driven SKF workflows — this skill is a validation harness with score-driven step semantics, not a chain that produces new artifacts.
20
+
12
21
  ## Role
13
22
 
14
23
  You are a skill auditor and completeness analyst operating in Ferris's Audit mode. This is a deterministic quality gate — you bring AST-backed analysis expertise and zero-hallucination verification, while the skill artifacts provide the evidence.
@@ -18,11 +27,8 @@ You are a skill auditor and completeness analyst operating in Ferris's Audit mod
18
27
  These rules apply to every step in this workflow:
19
28
 
20
29
  - Zero hallucination — every finding must trace to actual code with file:line citations
21
- - Read each step file completely before taking any action
22
- - Follow the mandatory sequence in each step exactly — do not skip, reorder, or optimize
23
30
  - Only load one step file at a time — never preload future steps
24
31
  - Update `stepsCompleted` in output file frontmatter before loading next step
25
- - If any instruction references a subprocess or tool you lack, achieve the outcome in your main context thread
26
32
  - Always communicate in `{communication_language}`
27
33
  - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
28
34
 
@@ -30,23 +36,54 @@ These rules apply to every step in this workflow:
30
36
 
31
37
  | # | Step | File | Auto-proceed |
32
38
  |---|------|------|--------------|
33
- | 1 | Initialize & Load Skill | steps-c/step-01-init.md | Yes |
34
- | 2 | Detect Mode | steps-c/step-02-detect-mode.md | Yes |
35
- | 3 | Coverage Check | steps-c/step-03-coverage-check.md | Yes |
36
- | 4 | Coherence Check | steps-c/step-04-coherence-check.md | Yes |
37
- | 4b | External Validators | steps-c/step-04b-external-validators.md | Yes |
38
- | 5 | Score | steps-c/step-05-score.md | Yes |
39
- | 6 | Report | steps-c/step-06-report.md | No (confirm) |
40
- | 7 | Workflow Health Check | steps-c/step-07-health-check.md | Yes |
39
+ | 1 | Initialize & Load Skill | references/init.md | Yes |
40
+ | 2 | Detect Mode | references/detect-mode.md | Yes |
41
+ | 3 | Coverage Check | references/coverage-check.md | Yes |
42
+ | 4 | Coherence Check | references/coherence-check.md | Yes |
43
+ | 4b | External Validators | references/external-validators.md | Yes |
44
+ | 5 | Score | references/score.md | Yes |
45
+ | 6 | Report | references/report.md | No (confirm) |
46
+ | 7 | Workflow Health Check | references/health-check.md | Yes |
41
47
 
42
48
  ## Invocation Contract
43
49
 
44
50
  | Aspect | Detail |
45
51
  |--------|--------|
46
- | **Inputs** | skill_name [required] |
47
- | **Gates** | step-06: Confirm Gate [C] |
48
- | **Outputs** | test-report-{skill_name}.md with completeness score and result (PASS/FAIL) |
52
+ | **Inputs** | skill_name [required]; optional flags: `--allow-workspace-drift`, `--no-discovery` (skip §4b Discovery Testing), `--no-health-check` (skip §7 health-check dispatch), `--tier=<Quick\|Forge\|Forge+\|Deep>` (bypass forge-tier.yaml sidecar requirement), `--threshold=<N>` (override pass threshold; CLI wins over `workflow.default_threshold` scalar) |
53
+ | **Gates** | step 6: Confirm Gate [C] |
54
+ | **Outputs** | test-report-{skill_name}.md with completeness score and result (PASS/FAIL); per-run `skf-test-skill-result-{run_id}.json` and `skf-test-skill-result-latest.json` written atomically under `{forge_version}/` |
49
55
  | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true |
56
+ | **Exit codes** | See "Exit Codes" below |
57
+
58
+ ## Exit Codes
59
+
60
+ Every terminal state in this workflow exits with a stable code so headless automators can branch on the verdict (and any HARD HALT) without grepping message text:
61
+
62
+ | Code | Meaning | Raised by |
63
+ | ---- | -------------------- | ------------------------------------------------------------------------------------------ |
64
+ | 0 | success / PASS | step 6 §6b — `testResult: 'pass'` (after the result contract is written in §4c) |
65
+ | 2 | fail / FAIL | step 6 §6b — `testResult: 'fail'` (after the result contract is written in §4c) |
66
+ | 3 | inconclusive | step 6 §6b — `testResult: 'inconclusive'` (distinct from fail so orchestrators can route to manual-review queues) |
67
+ | 4 | pass-with-drift | step 6 §6b — `testResult: 'pass-with-drift'` (distinct from clean pass — `--allow-workspace-drift` was in effect; orchestrators MUST route to re-test-against-pinned-commit queues and refuse export; never exit 0 under drift override) |
68
+
69
+ ## Result Contract (Headless)
70
+
71
+ When `{headless_mode}` is true, step 6 emits a single-line JSON envelope on **stdout** before chaining to step 7, and every HARD HALT emits the same envelope shape on **stderr** with `status: "error"`:
72
+
73
+ ```
74
+ SKF_TEST_RESULT_JSON: {"status":"success|error","skill_name":"…","verdict":"PASS|FAIL|INCONCLUSIVE|pass-with-drift","score":N,"threshold":N,"report_path":"…|null","next_workflow":"export-skill|update-skill|null","exit_code":0,"halt_reason":null}
75
+ ```
76
+
77
+ `status` is `"success"` on the terminal happy path (any of PASS / FAIL / INCONCLUSIVE / pass-with-drift — the workflow completed), `"error"` on any HARD HALT before §4c. `verdict` is the canonical result string. `next_workflow` is `"export-skill"` only when `verdict == "PASS"`; `"update-skill"` for `FAIL` or `pass-with-drift`; `null` for `INCONCLUSIVE` (manual review). `halt_reason` is `null` on the terminal path or one of the workflow-defined halt strings (`"forge-tier-missing"`, `"target-inaccessible"`, `"write-failed"`, `"step-completeness-violation"`, `"report-anchor-missing"`, `"health-check-missing"`, `"atomic-writer-missing"`). `exit_code` matches the table above.
78
+
79
+ The same payload is persisted to disk by step 6 §4c (atomic write) at two locations under `{forge_version}/`:
80
+
81
+ | Path | Purpose |
82
+ | --------------------------------------------- | -------------------------------------------------------------------------- |
83
+ | `skf-test-skill-result-{run_id}.json` | Per-run record. `{run_id}` carries UTC timestamp + PID + random suffix. |
84
+ | `skf-test-skill-result-latest.json` | Latest copy — stable path for pipeline consumers (copy, not symlink). |
85
+
86
+ The on-disk payload is the richer form: it adds `outputs[]` (report-path entries), `summary` (`score`, `threshold`, `result`, `testMode`, `activeCategories[]`, `inconclusiveReasons[]` when present), `runId`, and `healthCheckDispatched`. The stdout envelope is the compact subset documented above.
50
87
 
51
88
  ## On Activation
52
89
 
@@ -56,4 +93,29 @@ These rules apply to every step in this workflow:
56
93
 
57
94
  2. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in preferences.yaml. Default: false.
58
95
 
59
- 3. Load, read the full file, and then execute `./steps-c/step-01-init.md` to begin the workflow.
96
+ 3. **Resolve workflow customization.** Run:
97
+
98
+ ```bash
99
+ python3 {project-root}/_bmad/scripts/resolve_customization.py \
100
+ --skill {skill-root} --key workflow
101
+ ```
102
+
103
+ The script merges the three customization layers per `bmad-customize`'s structural merge rules (scalars override, arrays append):
104
+
105
+ - `{skill-root}/customize.toml` — bundled defaults
106
+ - `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
107
+ - `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
108
+
109
+ 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.
110
+
111
+ Apply the path-scalar fallback now so stage files don't have to repeat the conditional logic. For each of the three scalars, if the merged value is empty or absent, use the bundled default:
112
+
113
+ - `{testReportTemplatePath}` ← `workflow.test_report_template_path` if non-empty, else `templates/test-report-template.md`
114
+ - `{outputFormatsPath}` ← `workflow.output_formats_path` if non-empty, else `assets/output-section-formats.md`
115
+ - `{scoringRulesPath}` ← `workflow.scoring_rules_path` if non-empty, else `references/scoring-rules.md`
116
+ - `{defaultThreshold}` ← `workflow.default_threshold` if non-empty/non-null, else `80`. CLI `--threshold=<N>` (when present) wins over the scalar at the usage site in `references/score.md`.
117
+ - `{onCompleteCommand}` ← `workflow.on_complete` if non-empty, else empty string. When empty, the post-finalization hook in `references/report.md` is a no-op.
118
+
119
+ Stash all five as workflow-context variables. Stage files reference `{testReportTemplatePath}` / `{outputFormatsPath}` / `{scoringRulesPath}` / `{defaultThreshold}` / `{onCompleteCommand}` directly — no conditional at the usage site. Empty-string overrides cleanly fall through to the bundled default; non-empty values let orgs swap in house-style copies without forking the skill.
120
+
121
+ 4. Load, read the full file, and then execute `references/init.md` to begin the workflow.
@@ -0,0 +1,54 @@
1
+ # DO NOT EDIT -- overwritten on every update.
2
+ #
3
+ # Workflow customization surface for skf-test-skill.
4
+
5
+ [workflow]
6
+
7
+ # --- Configurable below. Overrides merge per BMad structural rules: ---
8
+ # scalars: override wins • arrays (persistent_facts, activation_steps_*): append
9
+ # arrays-of-tables with `code`/`id`: replace matching items, append new ones.
10
+
11
+ # Steps to run before the standard activation (uv probe, config load).
12
+ # Overrides append. Use for org-wide pre-flight checks (auth, network,
13
+ # compliance) that must precede any quality-test work.
14
+
15
+ activation_steps_prepend = []
16
+
17
+ # Steps to run after activation but before the first stage executes.
18
+ # Overrides append. Use for context loads or banner customization that
19
+ # should run once activation completes successfully.
20
+
21
+ activation_steps_append = []
22
+
23
+ # Persistent facts the workflow keeps in mind for the whole run
24
+ # (house style, severity policies, grading guardrails).
25
+ # Overrides append.
26
+ #
27
+ # Each entry is either:
28
+ # - a literal sentence, e.g. "Test failures of CRITICAL severity block release."
29
+ # - a file reference prefixed with `file:`, e.g.
30
+ # "file:{project-root}/docs/test-policy.md" (globs supported; file
31
+ # contents are loaded and treated as facts).
32
+
33
+ persistent_facts = [
34
+ "file:{project-root}/**/project-context.md",
35
+ ]
36
+
37
+ # --- Optional asset overrides ---
38
+ #
39
+ # Lift the canonical asset paths so orgs can substitute house-style copies
40
+ # without forking the skill. Empty string = use the bundled default.
41
+
42
+ test_report_template_path = ""
43
+ output_formats_path = ""
44
+ scoring_rules_path = ""
45
+
46
+ # Pass threshold (percent). CLI `--threshold=<N>` wins over this scalar.
47
+ # Override here to change the org-wide default without forking the skill.
48
+ default_threshold = 80
49
+
50
+ # Optional post-finalization hook. When non-empty, the workflow invokes:
51
+ # <on_complete> --result-path=<path-to-skf-test-skill-result-{run_id}.json>
52
+ # after the result JSON is finalized. Failures are recorded to
53
+ # workflow_warnings[] but never fail the workflow. Empty = no-op.
54
+ on_complete = ""