bmad-module-skill-forge 1.4.1 → 1.5.1

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 +147 -16
  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-manifest-ops.py +22 -6
  35. package/src/shared/scripts/skf-merge-ccc-exclusions.py +34 -4
  36. package/src/shared/scripts/skf-pair-intersect.py +250 -0
  37. package/src/shared/scripts/skf-provenance-gap-dispatch.py +433 -0
  38. package/src/shared/scripts/skf-qmd-classify-collections.py +42 -8
  39. package/src/shared/scripts/skf-recommend-scope-type.py +2 -2
  40. package/src/shared/scripts/skf-render-quick-metadata.py +2 -2
  41. package/src/shared/scripts/skf-resolve-authoritative-files.py +504 -0
  42. package/src/shared/scripts/skf-scan-manifests.py +738 -0
  43. package/src/shared/scripts/skf-scan-skill-md-structure.py +375 -0
  44. package/src/shared/scripts/skf-update-active-symlink.py +315 -0
  45. package/src/shared/scripts/skf-validate-brief-inputs.py +5 -5
  46. package/src/shared/scripts/skf-validate-brief-schema.py +348 -0
  47. package/src/shared/scripts/skf-write-skill-brief.py +61 -5
  48. package/src/skf-analyze-source/SKILL.md +69 -16
  49. package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
  50. package/src/skf-analyze-source/customize.toml +55 -0
  51. package/src/skf-analyze-source/{steps-c/step-01b-continue.md → references/continue.md} +12 -12
  52. package/src/skf-analyze-source/{steps-c/step-06-generate-briefs.md → references/generate-briefs.md} +29 -10
  53. package/src/skf-analyze-source/{steps-c/step-07-health-check.md → references/health-check.md} +5 -3
  54. package/src/skf-analyze-source/{steps-c/step-03-identify-units.md → references/identify-units.md} +48 -14
  55. package/src/skf-analyze-source/{steps-c/step-01-init.md → references/init.md} +32 -9
  56. package/src/skf-analyze-source/{steps-c/step-04-map-and-detect.md → references/map-and-detect.md} +52 -25
  57. package/src/skf-analyze-source/{steps-c/step-05-recommend.md → references/recommend.md} +15 -8
  58. package/src/skf-analyze-source/{steps-c/step-02-scan-project.md → references/scan-project.md} +16 -10
  59. package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -0
  60. package/src/skf-analyze-source/templates/analysis-report-template.md +6 -6
  61. package/src/skf-audit-skill/SKILL.md +66 -15
  62. package/src/skf-audit-skill/assets/drift-report-template.md +6 -5
  63. package/src/skf-audit-skill/customize.toml +49 -0
  64. package/src/skf-audit-skill/{steps-c/step-07-health-check.md → references/health-check.md} +5 -3
  65. package/src/skf-audit-skill/{steps-c/step-01-init.md → references/init.md} +63 -19
  66. package/src/skf-audit-skill/{steps-c/step-02-re-index.md → references/re-index.md} +11 -11
  67. package/src/skf-audit-skill/{steps-c/step-06-report.md → references/report.md} +25 -7
  68. package/src/skf-audit-skill/{steps-c/step-04-semantic-diff.md → references/semantic-diff.md} +6 -6
  69. package/src/skf-audit-skill/{steps-c/step-05-severity-classify.md → references/severity-classify.md} +5 -5
  70. package/src/skf-audit-skill/references/severity-rules.md +2 -0
  71. package/src/skf-audit-skill/{steps-c/step-03-structural-diff.md → references/structural-diff.md} +35 -17
  72. package/src/skf-brief-skill/SKILL.md +49 -19
  73. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  74. package/src/skf-brief-skill/assets/skill-brief-schema.md +32 -2
  75. package/src/skf-brief-skill/customize.toml +44 -0
  76. package/src/skf-brief-skill/{steps-c/step-02-analyze-target.md → references/analyze-target.md} +27 -15
  77. package/src/skf-brief-skill/{steps-c/step-04-confirm-brief.md → references/confirm-brief.md} +13 -10
  78. package/src/skf-brief-skill/references/draft-checkpoint.md +4 -4
  79. package/src/skf-brief-skill/{steps-c/step-01-gather-intent.md → references/gather-intent.md} +35 -29
  80. package/src/skf-brief-skill/references/headless-args.md +9 -9
  81. package/src/skf-brief-skill/references/headless-source-authority-detection.md +1 -1
  82. package/src/skf-brief-skill/{steps-c/step-06-health-check.md → references/health-check.md} +4 -2
  83. package/src/skf-brief-skill/references/portfolio-similarity-check.md +2 -2
  84. package/src/skf-brief-skill/references/qmd-collection-registration.md +2 -2
  85. package/src/skf-brief-skill/{steps-c/step-03-scope-definition.md → references/scope-definition.md} +27 -20
  86. package/src/skf-brief-skill/references/version-resolution.md +2 -2
  87. package/src/skf-brief-skill/{steps-c/step-05-write-brief.md → references/write-brief.md} +29 -16
  88. package/src/skf-create-skill/SKILL.md +25 -19
  89. package/src/skf-create-skill/assets/compile-assembly-rules.md +3 -3
  90. package/src/skf-create-skill/assets/skill-sections.md +5 -5
  91. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +11 -11
  92. package/src/skf-create-skill/customize.toml +35 -0
  93. package/src/skf-create-skill/references/authoritative-files-protocol.md +142 -0
  94. package/src/skf-create-skill/{steps-c/step-05-compile.md → references/compile.md} +16 -16
  95. package/src/skf-create-skill/{steps-c/step-03d-component-extraction.md → references/component-extraction.md} +16 -16
  96. package/src/skf-create-skill/{steps-c/step-02-ecosystem-check.md → references/ecosystem-check.md} +4 -4
  97. package/src/skf-create-skill/{steps-c/step-04-enrich.md → references/enrich.md} +3 -3
  98. package/src/skf-create-skill/{steps-c/step-03-extract.md → references/extract.md} +52 -120
  99. package/src/skf-create-skill/references/extraction-patterns.md +10 -8
  100. package/src/skf-create-skill/{steps-c/step-07-generate-artifacts.md → references/generate-artifacts.md} +11 -11
  101. package/src/skf-create-skill/{steps-c/step-09-health-check.md → references/health-check.md} +4 -2
  102. package/src/skf-create-skill/{steps-c/step-01-load-brief.md → references/load-brief.md} +40 -32
  103. package/src/skf-create-skill/{steps-c/step-08-report.md → references/report.md} +8 -8
  104. package/src/skf-create-skill/references/source-resolution-protocols.md +6 -6
  105. package/src/skf-create-skill/{steps-c/sub/step-02b-ccc-discover.md → references/sub/ccc-discover.md} +5 -5
  106. package/src/skf-create-skill/{steps-c/sub/step-03c-fetch-docs.md → references/sub/fetch-docs.md} +28 -12
  107. package/src/skf-create-skill/{steps-c/sub/step-03b-fetch-temporal.md → references/sub/fetch-temporal.md} +81 -50
  108. package/src/skf-create-skill/{steps-c/step-06-validate.md → references/validate.md} +48 -28
  109. package/src/skf-create-stack-skill/SKILL.md +76 -17
  110. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +102 -0
  111. package/src/skf-create-stack-skill/assets/stack-skill-template.md +1 -1
  112. package/src/skf-create-stack-skill/customize.toml +46 -0
  113. package/src/skf-create-stack-skill/{steps-c/step-06-compile-stack.md → references/compile-stack.md} +6 -5
  114. package/src/skf-create-stack-skill/references/compose-mode-rules.md +3 -1
  115. package/src/skf-create-stack-skill/{steps-c/step-05-detect-integrations.md → references/detect-integrations.md} +37 -11
  116. package/src/skf-create-stack-skill/{steps-c/step-02-detect-manifests.md → references/detect-manifests.md} +32 -28
  117. package/src/skf-create-stack-skill/{steps-c/step-07-generate-output.md → references/generate-output.md} +11 -85
  118. package/src/skf-create-stack-skill/{steps-c/step-10-health-check.md → references/health-check.md} +4 -2
  119. package/src/skf-create-stack-skill/{steps-c/step-01-init.md → references/init.md} +70 -4
  120. package/src/skf-create-stack-skill/references/integration-patterns.md +2 -0
  121. package/src/skf-create-stack-skill/references/manifest-patterns.md +2 -0
  122. package/src/skf-create-stack-skill/{steps-c/step-04-parallel-extract.md → references/parallel-extract.md} +44 -17
  123. package/src/skf-create-stack-skill/{steps-c/step-03-rank-and-confirm.md → references/rank-and-confirm.md} +5 -4
  124. package/src/skf-create-stack-skill/{steps-c/step-09-report.md → references/report.md} +3 -3
  125. package/src/skf-create-stack-skill/{steps-c/step-08-validate.md → references/validate.md} +6 -6
  126. package/src/skf-drop-skill/SKILL.md +84 -12
  127. package/src/skf-drop-skill/customize.toml +57 -0
  128. package/src/skf-drop-skill/{steps-c/step-02-execute.md → references/execute.md} +49 -34
  129. package/src/skf-drop-skill/{steps-c/step-04-health-check.md → references/health-check.md} +6 -4
  130. package/src/skf-drop-skill/{steps-c/step-03-report.md → references/report.md} +13 -5
  131. package/src/skf-drop-skill/{steps-c/step-01-select.md → references/select.md} +63 -15
  132. package/src/skf-export-skill/SKILL.md +85 -16
  133. package/src/skf-export-skill/assets/managed-section-format.md +3 -3
  134. package/src/skf-export-skill/assets/snippet-format.md +2 -2
  135. package/src/skf-export-skill/customize.toml +49 -0
  136. package/src/skf-export-skill/{steps-c/step-03-generate-snippet.md → references/generate-snippet.md} +11 -11
  137. package/src/skf-export-skill/{steps-c/step-07-health-check.md → references/health-check.md} +6 -4
  138. package/src/skf-export-skill/{steps-c/step-01-load-skill.md → references/load-skill.md} +11 -49
  139. package/src/skf-export-skill/references/manifest-rebuild.md +68 -0
  140. package/src/skf-export-skill/references/multi-skill-mode.md +38 -0
  141. package/src/skf-export-skill/references/orphan-context-detection.md +75 -0
  142. package/src/skf-export-skill/references/orphan-row-detection.md +102 -0
  143. package/src/skf-export-skill/{steps-c/step-02-package.md → references/package.md} +7 -7
  144. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +79 -0
  145. package/src/skf-export-skill/{steps-c/step-06-summary.md → references/summary.md} +23 -11
  146. package/src/skf-export-skill/{steps-c/step-05-token-report.md → references/token-report.md} +5 -5
  147. package/src/skf-export-skill/{steps-c/step-04-update-context.md → references/update-context.md} +109 -113
  148. package/src/skf-quick-skill/SKILL.md +63 -123
  149. package/src/skf-quick-skill/customize.toml +44 -0
  150. package/src/skf-quick-skill/references/batch-mode.md +102 -0
  151. package/src/skf-quick-skill/{steps-c/step-04-compile.md → references/compile.md} +10 -10
  152. package/src/skf-quick-skill/{steps-c/step-02-ecosystem-check.md → references/ecosystem-check.md} +5 -5
  153. package/src/skf-quick-skill/{steps-c/step-06-finalize.md → references/finalize.md} +7 -7
  154. package/src/skf-quick-skill/{steps-c/step-07-health-check.md → references/health-check.md} +3 -3
  155. package/src/skf-quick-skill/{steps-c/step-03-quick-extract.md → references/quick-extract.md} +5 -5
  156. package/src/skf-quick-skill/references/registry-resolution.md +2 -0
  157. package/src/skf-quick-skill/{steps-c/step-01-resolve-target.md → references/resolve-target.md} +14 -10
  158. package/src/skf-quick-skill/{steps-c/step-05-write-and-validate.md → references/write-and-validate.md} +4 -4
  159. package/src/skf-refine-architecture/SKILL.md +86 -16
  160. package/src/skf-refine-architecture/customize.toml +49 -0
  161. package/src/skf-refine-architecture/{steps-c/step-05-compile.md → references/compile.md} +4 -4
  162. package/src/skf-refine-architecture/{steps-c/step-02-gap-analysis.md → references/gap-analysis.md} +5 -5
  163. package/src/skf-refine-architecture/{steps-c/step-07-health-check.md → references/health-check.md} +6 -4
  164. package/src/skf-refine-architecture/{steps-c/step-04-improvements.md → references/improvements.md} +5 -5
  165. package/src/skf-refine-architecture/references/init.md +144 -0
  166. package/src/skf-refine-architecture/{steps-c/step-03-issue-detection.md → references/issue-detection.md} +5 -5
  167. package/src/skf-refine-architecture/references/refinement-rules.md +2 -0
  168. package/src/skf-refine-architecture/{steps-c/step-06-report.md → references/report.md} +14 -5
  169. package/src/skf-rename-skill/SKILL.md +82 -12
  170. package/src/skf-rename-skill/customize.toml +52 -0
  171. package/src/skf-rename-skill/{steps-c/step-02-execute.md → references/execute.md} +94 -99
  172. package/src/skf-rename-skill/references/health-check.md +30 -0
  173. package/src/skf-rename-skill/references/rebuild-context.md +110 -0
  174. package/src/skf-rename-skill/{steps-c/step-03-report.md → references/report.md} +13 -5
  175. package/src/skf-rename-skill/{steps-c/step-01-select.md → references/select.md} +59 -20
  176. package/src/skf-setup/SKILL.md +35 -35
  177. package/src/skf-setup/customize.toml +33 -0
  178. package/src/skf-setup/{steps-c/step-03-auto-index.md → references/auto-index.md} +10 -18
  179. package/src/skf-setup/{steps-c/step-01b-ccc-index.md → references/ccc-index.md} +16 -31
  180. package/src/skf-setup/{steps-c/step-01-detect-and-tier.md → references/detect-and-tier.md} +23 -10
  181. package/src/skf-setup/{steps-c/step-05-health-check.md → references/health-check.md} +1 -1
  182. package/src/skf-setup/{steps-c/step-04-report.md → references/report.md} +21 -19
  183. package/src/skf-setup/references/tier-rules.md +1 -1
  184. package/src/skf-setup/{steps-c/step-02-write-config.md → references/write-config.md} +14 -13
  185. package/src/skf-test-skill/SKILL.md +77 -15
  186. package/src/skf-test-skill/customize.toml +54 -0
  187. package/src/skf-test-skill/{steps-c/step-04-coherence-check.md → references/coherence-check.md} +37 -73
  188. package/src/skf-test-skill/{steps-c/step-03-coverage-check.md → references/coverage-check.md} +16 -39
  189. package/src/skf-test-skill/{steps-c/step-02-detect-mode.md → references/detect-mode.md} +4 -26
  190. package/src/skf-test-skill/{steps-c/step-04b-external-validators.md → references/external-validators.md} +6 -28
  191. package/src/skf-test-skill/references/health-check.md +14 -0
  192. package/src/skf-test-skill/{steps-c/step-01-init.md → references/init.md} +21 -37
  193. package/src/skf-test-skill/references/migration-section-rules.md +4 -2
  194. package/src/skf-test-skill/{steps-c/step-06-report.md → references/report.md} +41 -51
  195. package/src/skf-test-skill/{steps-c/step-05-score.md → references/score.md} +26 -43
  196. package/src/skf-test-skill/references/scoring-rules.md +8 -6
  197. package/src/skf-test-skill/references/source-access-protocol.md +7 -5
  198. package/src/skf-test-skill/scripts/compute-score.py +88 -17
  199. package/src/skf-test-skill/templates/test-report-template.md +13 -13
  200. package/src/skf-update-skill/SKILL.md +23 -14
  201. package/src/skf-update-skill/customize.toml +44 -0
  202. package/src/skf-update-skill/{steps-c/step-02-detect-changes.md → references/detect-changes.md} +176 -68
  203. package/src/skf-update-skill/references/health-check.md +32 -0
  204. package/src/skf-update-skill/{steps-c/step-01-init.md → references/init.md} +56 -8
  205. package/src/skf-update-skill/references/manual-section-rules.md +4 -0
  206. package/src/skf-update-skill/references/merge-conflict-rules.md +4 -0
  207. package/src/skf-update-skill/{steps-c/step-04-merge.md → references/merge.md} +8 -8
  208. package/src/skf-update-skill/{steps-c/step-03-re-extract.md → references/re-extract.md} +53 -44
  209. package/src/skf-update-skill/references/remote-source-resolution.md +5 -1
  210. package/src/skf-update-skill/references/report.md +214 -0
  211. package/src/skf-update-skill/{steps-c/step-05-validate.md → references/validate.md} +5 -5
  212. package/src/skf-update-skill/{steps-c/step-06-write.md → references/write.md} +73 -51
  213. package/src/skf-verify-stack/SKILL.md +89 -17
  214. package/src/skf-verify-stack/assets/feasibility-report-template.md +4 -4
  215. package/src/skf-verify-stack/customize.toml +50 -0
  216. package/src/skf-verify-stack/references/coverage-patterns.md +2 -2
  217. package/src/skf-verify-stack/{steps-c/step-02-coverage.md → references/coverage.md} +13 -9
  218. package/src/skf-verify-stack/{steps-c/step-07-health-check.md → references/health-check.md} +7 -5
  219. package/src/skf-verify-stack/references/init.md +170 -0
  220. package/src/skf-verify-stack/references/integration-verification-rules.md +1 -1
  221. package/src/skf-verify-stack/{steps-c/step-03-integrations.md → references/integrations.md} +18 -14
  222. package/src/skf-verify-stack/{steps-c/step-06-report.md → references/report.md} +27 -42
  223. package/src/skf-verify-stack/{steps-c/step-04-requirements.md → references/requirements.md} +13 -9
  224. package/src/skf-verify-stack/{steps-c/step-05-synthesize.md → references/synthesize.md} +14 -10
  225. package/tools/validate-docs-drift.js +1 -1
  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-03c-fetch-docs.md'
2
+ nextStepFile: 'fetch-docs.md'
3
3
  # Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order
4
4
  # (installed SKF module path first, src/ dev-checkout fallback); first existing
5
5
  # path wins. HALT if neither resolves.
@@ -8,23 +8,23 @@ atomicWriteProbeOrder:
8
8
  - '{project-root}/src/shared/scripts/skf-atomic-write.py'
9
9
  ---
10
10
 
11
+ <!-- Config: communicate in {communication_language}. -->
12
+
11
13
  # Step 3b: Fetch Temporal Context
12
14
 
13
15
  ## STEP GOAL:
14
16
 
15
- To fetch temporal context (issues, PRs, changelogs, release notes) from the source repository and index it into a QMD collection for Deep tier enrichment. This ensures step-04 has historical data to search when annotating extracted functions with T2 provenance.
17
+ To fetch temporal context (issues, PRs, changelogs, release notes) from the source repository and index it into a QMD collection for Deep tier enrichment. This ensures step 4 has historical data to search when annotating extracted functions with T2 provenance.
16
18
 
17
19
  ## Rules
18
20
 
19
21
  - Deep tier only — Quick, Forge, and Forge+ tiers skip this step entirely and silently
20
22
  - GitHub repositories only — other source types degrade gracefully
21
23
  - Do not halt the workflow if fetching or indexing fails
22
- - Do not modify extraction data from step-03 — this step only creates QMD collections
24
+ - Do not modify extraction data from step 3 — this step only creates QMD collections
23
25
 
24
26
  ## MANDATORY SEQUENCE
25
27
 
26
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
27
-
28
28
  ### 1. Check Eligibility
29
29
 
30
30
  Evaluate the following conditions sequentially. **If ANY condition fails, skip silently to section 5 (auto-proceed) with no output:**
@@ -69,57 +69,65 @@ Create a staging directory: `_bmad-output/{skill-name}-temporal/`
69
69
 
70
70
  Resolve the `owner` and `repo` from `source_repo` (e.g., `acme/toolkit` from `https://github.com/acme/toolkit`).
71
71
 
72
- Execute the following fetches, writing output as markdown files to the staging directory. **If any individual fetch fails, log a warning and continue with the others:**
73
-
74
- 1. **Issues (last 100):** (rationale: 100 is `gh issue list`'s default max-per-page; a single paginated call captures recent activity without extra round trips or rate-limit pressure)
72
+ Execute the four fetches below **in parallel** they are independent and the network round-trips dominate wall-clock. Background each, then `wait` for the batch. **If any individual fetch fails, log a warning and continue with the others.** The 4-concurrent fan-out is well under GitHub's authenticated REST rate limit (5000/hr); no bounded-concurrency guard is needed for this set.
75
73
 
76
- ```bash
77
- gh issue list -R {owner}/{repo} --state all --limit 100 --json number,title,state,labels,createdAt,closedAt,body | ...
78
- ```
74
+ ```bash
75
+ mkdir -p {staging}
76
+ # 1. Issues (last 100)
77
+ ( gh issue list -R {owner}/{repo} --state all --limit 100 \
78
+ --json number,title,state,labels,createdAt,closedAt,body \
79
+ | jq -r '...' > {staging}/issues.md ) &
80
+ # 2. Merged PRs (last 100)
81
+ ( gh pr list -R {owner}/{repo} --state merged --limit 100 \
82
+ --json number,title,mergedAt,labels,body \
83
+ | jq -r '...' > {staging}/prs.md ) &
84
+ # 3. Release tags only (the per-tag fetch loop runs sequentially below to
85
+ # preserve append-immediately crash-resume semantics for releases.md)
86
+ ( gh release list -R {owner}/{repo} --limit 10 \
87
+ --json tagName,name,publishedAt > {staging}/.release-tags.json ) &
88
+ # 4. Changelog (404 is silent skip — note `set +e` so the subshell doesn't
89
+ # propagate `gh api`'s non-zero exit on missing file)
90
+ ( set +e
91
+ gh api repos/{owner}/{repo}/contents/CHANGELOG.md --jq '.content' \
92
+ | base64 -d > {staging}/changelog.md 2>/dev/null
93
+ [ -s {staging}/changelog.md ] || rm -f {staging}/changelog.md ) &
94
+ wait
95
+ ```
79
96
 
80
- Write to `{staging}/issues.md` — format as a markdown document with one section per issue (number, title, state, labels, body summary).
97
+ Per-call rationale:
81
98
 
82
- 2. **Merged PRs (last 100):** (rationale: same 100-per-page convention as issues captures the most recent merges in one API call)
99
+ 1. **Issues (last 100):** 100 is `gh issue list`'s default max-per-page; one paginated call captures recent activity without extra round trips or rate-limit pressure. Output → `{staging}/issues.md` formatted as a markdown document with one section per issue.
83
100
 
84
- ```bash
85
- gh pr list -R {owner}/{repo} --state merged --limit 100 --json number,title,mergedAt,labels,body | ...
86
- ```
101
+ 2. **Merged PRs (last 100):** Same 100-per-page convention as issues — captures the most recent merges in one API call. Output → `{staging}/prs.md`.
87
102
 
88
- Write to `{staging}/prs.md` format as a markdown document with one section per PR.
103
+ 3. **Release tags + per-release fetches (last 10):** Release notes accumulate slowly relative to issues/PRs; the most recent 10 tags cover roughly the last 6-18 months of changelog-relevant history for typical OSS projects, which is enough context for T2-past annotations without fanning out to dozens of `gh release view` calls.
89
104
 
90
- 3. **Releases (last 10):** (rationale: release notes accumulate slowly relative to issues/PRs; the most recent 10 tags cover roughly the last 6-18 months of changelog-relevant history for typical OSS projects, which is enough context for T2-past annotations without fanning out to dozens of `gh release view` calls)
105
+ **Note:** `gh release list --json` does **not** support the `body` field. The parallel block above fetches tags only (Step 1). After `wait`, run **Step 2 sequentially** to preserve the append-immediately crash-resume contract:
91
106
 
92
- **Note:** `gh release list --json` does **not** support the `body` field. Use a two-step approach: list tags first, then fetch each release individually with `--json` (which IS supported on `gh release view`).
107
+ If `{staging}/.release-tags.json` is empty (no releases), skip Step 2 and omit the releases section entirely. Otherwise:
93
108
 
94
109
  ```bash
95
- # Step 1: Get release tags (body NOT available here)
96
- gh release list -R {owner}/{repo} --limit 10 --json tagName,name,publishedAt
110
+ # Sequential per-tag loop. Iterate the JSON tag array verbatim so a
111
+ # crash mid-loop leaves a partial-but-well-formed releases.md on disk.
112
+ echo "# Releases (partial if interrupted)" > {staging}/releases.md
113
+ ERR_FILE="{staging}/.gh-release-err" # per-run stderr capture inside staging
114
+ jq -r '.[].tagName' {staging}/.release-tags.json | while IFS= read -r tag; do
115
+ if gh release view "$tag" -R {owner}/{repo} \
116
+ --json tagName,name,publishedAt,body 2>"$ERR_FILE"; then
117
+ jq -r '...' >> {staging}/releases.md # one ## {tag} block per release
118
+ else
119
+ echo "## $tag — fetch failed: $(cat "$ERR_FILE")" \
120
+ >> {staging}/releases.md
121
+ fi
122
+ done
123
+ rm -f "$ERR_FILE"
97
124
  ```
98
125
 
99
- If Step 1 returns an empty array (no releases), skip Step 2 and omit the releases section entirely.
126
+ Failed individual fetches get a one-line placeholder; the loop continues with remaining tags. If a rate limit (HTTP 429) is hit, stop the release loop, keep the partial `releases.md` file in place (do NOT delete it), and log: "Release fetch stopped at tag {N}/{total} due to rate limiting — partial releases.md retained."
100
127
 
101
- ```bash
102
- # Step 2: For EACH tagName from Step 1, fetch the full release
103
- gh release view {tagName} -R {owner}/{repo} --json tagName,name,publishedAt,body
104
- ```
128
+ **Why sequential here when the rest is parallel:** the append-per-release pattern guarantees that a mid-loop abort (rate limit, network drop, user interrupt) leaves a partial but well-formed `releases.md` with every release fetched so far. Parallel writers appending to the same file would need file locking and per-writer ordering — the simpler sequential loop is robust for free, and 10 release fetches contribute only ~5-10s of the total wall-clock.
105
129
 
106
- Iterate over every `tagName` from Step 1's JSON array. **Append each release to `{staging}/releases.md` immediately after its `gh release view` call returns** — do not buffer the entire loop in memory and write once at the end. The append-per-release pattern guarantees that a mid-loop abort (rate limit, network drop, user interrupt) leaves a partial but well-formed `releases.md` with every release fetched so far, rather than discarding all of them because the loop didn't reach its final write.
107
-
108
- Write an empty `{staging}/releases.md` with a header (`# Releases (partial if interrupted)`) before the loop, then append one `## {tagName} — {name} ({publishedAt})` section per successful fetch. Failed individual fetches get a one-line placeholder: `## {tagName} — fetch failed: {error}`.
109
-
110
- If `gh release view` fails for a specific tag, log a warning and skip that release — continue with remaining tags. If a rate limit (HTTP 429) is hit, stop the release loop, keep the partial `releases.md` file in place (do NOT delete it), and log: "Release fetch stopped at tag {N}/{total} due to rate limiting — partial releases.md retained."
111
-
112
- Format each section as a markdown block with tag, name, date, and body.
113
-
114
- 4. **Changelog (if exists):**
115
-
116
- Check if `CHANGELOG.md` or `RELEASES.md` exists in the repository root:
117
-
118
- ```bash
119
- gh api repos/{owner}/{repo}/contents/CHANGELOG.md --jq '.content' | base64 -d
120
- ```
121
-
122
- If found, write to `{staging}/changelog.md`. If not found (404), skip silently.
130
+ 4. **Changelog:** `CHANGELOG.md` or `RELEASES.md` at the repository root. The parallel block above writes to `{staging}/changelog.md` only when the file exists; non-existence (404 from `gh api`) leaves no file behind.
123
131
 
124
132
  #### 3b. Targeted Function Searches (Uses Extraction Inventory)
125
133
 
@@ -127,19 +135,42 @@ After the generic fetches above, perform **targeted searches** using the top-lev
127
135
 
128
136
  **Short-circuit on empty `top_exports`:** If `extraction_inventory.top_exports` is missing or `== []` (docs-only mode, or a source extraction that produced zero public exports), skip this sub-section entirely with a one-line log: "No exports in inventory — skipping targeted function searches." The generic fetches from §3 remain in place and continue to provide baseline temporal context.
129
137
 
130
- **Limit:** Search the top **10 function names** maximum to control API call volume and avoid `gh` rate limiting. (rationale: 10 targeted searches + generic fetches from §3 stays well under GitHub's unauthenticated search rate limit of 10 requests/minute and authenticated 30/minute; matches the `top_exports[]` size emitted by step-03 §5 so every tracked export gets one search.)
138
+ **Limit:** Search the top **10 function names** maximum to control API call volume and avoid `gh` rate limiting. (rationale: 10 targeted searches + generic fetches from §3 stays well under GitHub's unauthenticated search rate limit of 10 requests/minute and authenticated 30/minute; matches the `top_exports[]` size emitted by step 3 §5 so every tracked export gets one search.)
131
139
 
132
140
  For each function name in `top_exports[]` (up to 10), **sanitize first**: strip every character that is not in `[A-Za-z0-9_]` from `function_name` to produce `safe_name`. This prevents shell injection and `gh` query parser errors when an export name contains punctuation (e.g., `<T>`, `.method`, `::namespace`, quotes). If `safe_name` is empty after sanitization (the original was entirely punctuation — rare but possible for symbol exports), fall back to piping the original name through stdin via `--query-from-file -`-style indirection if your `gh` version supports it; otherwise skip that one entry with a log line — never substitute the unsanitized name back into the shell command.
133
141
 
142
+ **Parallel fan-out (bounded concurrency = 5):** sanitized searches are independent and benefit from parallelism. Use `xargs -P 5` so at most 5 `gh search` calls are in flight at once — this stays well under GitHub's authenticated search rate limit (30/min) while saving ~5-8s of wall-clock on a top-10 export list compared to fully sequential.
143
+
134
144
  ```bash
135
- # safe_name = re.sub(r'[^A-Za-z0-9_]', '', function_name); skip if empty
136
- # --limit 5: top-5 issues per function keeps signal-to-noise high (most matches
137
- # below rank 5 are typically keyword coincidences, not targeted discussions) and
138
- # caps the total response size across 10 function fan-outs at 50 issues.
139
- gh search issues --repo {owner}/{repo} "{safe_name}" --limit 5 --json number,title,state,body
145
+ # 1. Sanitize and write one safe_name per line; skip empties.
146
+ # Python is the canonical sanitizer because awk/sed regex semantics
147
+ # vary across platforms.
148
+ jq -r '.top_exports[]' {extraction_inventory.json} \
149
+ | python3 -c 'import sys,re
150
+ for line in sys.stdin:
151
+ s = re.sub(r"[^A-Za-z0-9_]", "", line.strip())
152
+ if s: print(s)' > {staging}/.safe-names.txt
153
+
154
+ # 2. Fan out to gh search, 5 in flight. Each writer emits a self-contained
155
+ # section to its own per-name file; we concatenate after the wait.
156
+ # --limit 5: top-5 issues per function keeps signal-to-noise high and caps
157
+ # total response size across 10 fan-outs at 50 issues.
158
+ mkdir -p {staging}/targeted
159
+ cat {staging}/.safe-names.txt | xargs -P 5 -I {} bash -c '
160
+ gh search issues --repo {owner}/{repo} "{}" --limit 5 \
161
+ --json number,title,state,body 2>/dev/null \
162
+ | jq -r ". | \"## {}\\n\" + (...)" > {staging}/targeted/{}.md \
163
+ || echo "## {} — fetch failed" > {staging}/targeted/{}.md
164
+ '
165
+
166
+ # 3. Concatenate in stable order (alpha by safe_name) into the single
167
+ # aggregated file the rest of the workflow expects.
168
+ sort {staging}/.safe-names.txt | while IFS= read -r name; do
169
+ cat "{staging}/targeted/$name.md"
170
+ done > {staging}/targeted-issues.md
140
171
  ```
141
172
 
142
- Aggregate all targeted search results into a single file: `{staging}/targeted-issues.md`. Format as a markdown document with one section per function name, listing the matching issues/PRs found.
173
+ Aggregate all targeted search results into a single file: `{staging}/targeted-issues.md`. The per-name temp directory `{staging}/targeted/` can be removed after concat it exists only to give each parallel writer an isolated output stream.
143
174
 
144
175
  **If `gh search` is unavailable** (older `gh` CLI versions): skip targeted searches silently. The generic fetches from section 3 still provide baseline temporal context.
145
176
 
@@ -1,6 +1,7 @@
1
1
  ---
2
- nextStepFile: './step-07-generate-artifacts.md'
2
+ nextStepFile: 'generate-artifacts.md'
3
3
  tesslDismissalData: 'assets/tessl-dismissal-rules.md'
4
+ descriptionGuardProtocol: '{project-root}/src/shared/references/description-guard-protocol.md'
4
5
  # Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order
5
6
  # (installed SKF module path first, src/ dev-checkout fallback); first existing
6
7
  # path wins. HALT if neither resolves — losing atomic-write guarantees is not
@@ -8,8 +9,18 @@ tesslDismissalData: 'assets/tessl-dismissal-rules.md'
8
9
  atomicWriteProbeOrder:
9
10
  - '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py'
10
11
  - '{project-root}/src/shared/scripts/skf-atomic-write.py'
12
+ # Resolve `{descriptionGuardHelper}` by probing `{descriptionGuardProbeOrder}`
13
+ # in order (installed SKF module path first, src/ dev-checkout fallback);
14
+ # first existing path wins. HALT if neither resolves — letting an external
15
+ # tool's rewrite of the description field stand would silently regress
16
+ # discovery quality.
17
+ descriptionGuardProbeOrder:
18
+ - '{project-root}/_bmad/skf/shared/scripts/skf-description-guard.py'
19
+ - '{project-root}/src/shared/scripts/skf-description-guard.py'
11
20
  ---
12
21
 
22
+ <!-- Config: communicate in {communication_language}. -->
23
+
13
24
  # Step 6: Validate
14
25
 
15
26
  ## STEP GOAL:
@@ -20,38 +31,19 @@ To validate the compiled SKILL.md content against the agentskills.io specificati
20
31
 
21
32
  - Focus only on validating compiled content against spec — only fix spec compliance issues
22
33
  - Validation and auto-fix modify files in the staging directory
23
- - `<staging-skill-dir>` resolves to `_bmad-output/{skill-name}/` as created by step-05. The directory name must match the skill's frontmatter `name` field exactly — `skill-check`'s `frontmatter.name_matches_directory` rule rejects any suffix.
34
+ - `<staging-skill-dir>` resolves to `_bmad-output/{skill-name}/` as created by step 5. The directory name must match the skill's frontmatter `name` field exactly — `skill-check`'s `frontmatter.name_matches_directory` rule rejects any suffix.
24
35
  - If skill-check unavailable: skip validation, add warning to evidence report
25
36
  - Ignore non-zero exit codes from skill-check if JSON output shows 0 errors
26
37
 
27
38
  ## MANDATORY SEQUENCE
28
39
 
29
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
30
-
31
40
  ### 0. Description Guard Protocol
32
41
 
33
42
  **Used by:** §2 (`skill-check check --fix`), §4 (`split-body`), and any future tool invocation that may modify SKILL.md.
34
43
 
35
- External validators occasionally rewrite the frontmatter `description` field `skill-check --fix` may replace it with a generic or truncated version, and `split-body` may touch it during mechanical restructuring. The step-05 §2a compiled description is **authoritative**: it has already been sanitized of angle-bracket tokens and trigger-optimized for agent discovery. Losing it to a tool's well-meaning rewrite breaks discovery quality and re-introduces the angle-bracket failure mode.
36
-
37
- To prevent this, any tool invocation that may touch SKILL.md must run inside the following four-phase guard:
38
-
39
- 1. **Capture.** Before invoking the tool, read the current SKILL.md frontmatter and snapshot the exact `description` value into a local variable (e.g., `guarded_description`). Capture the in-context copy as well.
40
- 2. **Execute.** Run the tool as specified in its section.
41
- 3. **Verify.** After the tool completes, re-read the on-disk SKILL.md and compare its frontmatter `description` against `guarded_description` as **token streams**: split each string on whitespace (`str.split()` — any run of spaces/tabs/newlines collapses) and compare the resulting lists element-by-element. This catches content divergence (missing words, replaced phrases, truncation, angle-bracket re-introduction) while ignoring cosmetic whitespace changes that a tool may apply (trailing newline, re-wrapped quoted strings). Do NOT use a normalized-string equality — a tool that rewrites `"foo bar"` to `"foo bar"` (collapsed inner run) would trip a naive normalization even though no semantic content changed, and a tool that swapped one word would slip past a looser fuzzy match. Token-stream comparison is the sweet spot.
42
- 4. **Restore on divergence.** If the post-tool description differs from `guarded_description` in any way other than whitespace normalization, write `guarded_description` back to the on-disk SKILL.md frontmatter and update the in-context copy to match. Record `description_guard_restored: true` with the tool name in context for the evidence report.
43
- 5. **Re-validate restored description.** After a restore, run `uv run {project-root}/src/shared/scripts/skf-validate-frontmatter.py <staging-skill-dir>/SKILL.md` against the on-disk file to confirm the restored description still satisfies the frontmatter contract (length limits, forbidden tokens, required fields). The script declares pyyaml in its PEP 723 inline metadata; `uv run` resolves it automatically (per `docs/getting-started.md`'s uv prereq), while bare `python3` would `ModuleNotFoundError` on a fresh interpreter. Capture `schema_revalidation_result` in context. If the validator exits non-zero OR reports failure for the `description` field: flip the Schema result back to `FAIL` in the evidence report (overriding any prior PASS/WARN from §2), record `description_guard_revalidation: FAIL` with the validator's diagnostic message, and continue — do not halt (step-09 health-check and result contract still need to run so the failure is surfaced through the normal artifact path).
44
-
45
- **What counts as divergence:**
44
+ Load `{descriptionGuardProtocol}` for the full prose explanation of the four-phase guard (why it exists, what counts as divergence, why token-stream comparison is the right shape). The deterministic phases are executed via `{descriptionGuardHelper}` the calling sections (§2 and §4) invoke the helper at the capture and verify-restore points.
46
45
 
47
- - The description was replaced (different content).
48
- - The description was truncated (suffix missing).
49
- - Angle-bracket tokens were re-introduced (should never happen after step-05 §2a, but protect anyway).
50
- - The field was deleted entirely (extreme tool behavior).
51
-
52
- **What does NOT count as divergence:** whitespace-only differences (trailing newline, trimmed spaces) — treat as equivalent.
53
-
54
- **Why this is centralized:** previously, §2 and §4 each contained their own capture/verify/restore prose. Duplicated defensive code drifts: a fix in one section doesn't propagate to the other, and adding a new tool invocation in the future requires remembering to copy the pattern. Centralizing the protocol gives step-06 one place to update when external validator behavior changes.
46
+ **This skill's post-restore re-validation hook:** after `{descriptionGuardHelper}` reports `restored: true`, run `uv run {project-root}/src/shared/scripts/skf-validate-frontmatter.py <staging-skill-dir>/SKILL.md` and capture `schema_revalidation_result` in context. If the validator exits non-zero OR reports failure for the `description` field, flip the Schema result back to `FAIL` in the evidence report (overriding any prior PASS/WARN from §2), record `description_guard_revalidation: FAIL` with the validator's diagnostic message, and continue — do not halt (step 9 health-check and result contract still need to run so the failure is surfaced through the normal artifact path).
55
47
 
56
48
  ### 1. Check Tool Availability
57
49
 
@@ -69,11 +61,11 @@ Run the external skill-check tool against the compiled skill staging directory.
69
61
  **Flag probe (run once, cache the result for §4 and §5 re-invocations):**
70
62
 
71
63
  ```bash
72
- npx skill-check --help 2>/dev/null | grep -- --no-security-scan
64
+ npx skill-check check --help 2>/dev/null | grep -- --no-security-scan
73
65
  ```
74
66
 
75
67
  - If the probe matches `--no-security-scan`: set `{security_scan_flag} = "--no-security-scan"`.
76
- - Else run a second probe — `npx skill-check --help 2>/dev/null | grep -- --skip-security` — and if it matches, set `{security_scan_flag} = "--skip-security"`.
68
+ - Else run a second probe — `npx skill-check check --help 2>/dev/null | grep -- --skip-security` — and if it matches, set `{security_scan_flag} = "--skip-security"`.
77
69
  - If neither flag exists: set `{security_scan_flag} = ""` (empty) AND set `{skill_check_flag_fallback} = true`. Skip §2 and §4 automated flows entirely — fall through to §3 manual frontmatter validation. Record in evidence-report: `skill_check_flag_probe: neither --no-security-scan nor --skip-security supported by installed skill-check; validation performed manually`.
78
70
 
79
71
  **If a security-scan-disable flag was resolved (probe succeeded):**
@@ -86,7 +78,21 @@ This performs frontmatter validation, description quality checks, body limit enf
86
78
 
87
79
  **Parse the JSON output** for: `qualityScore` (0-100), `diagnostics[]` (remaining issues), `fixed[]` (auto-corrected issues).
88
80
 
89
- **Description Guard Protocol:** This invocation may modify SKILL.md (especially when `fixed[]` is non-empty). Wrap the `skill-check check --fix` call in the four-phase protocol defined in §0: capture `guarded_description` before the call, execute, verify against the post-tool description, and restore on divergence. If `fixed[]` is non-empty, also re-read the modified SKILL.md to sync the in-context copy before proceeding — this prevents silent divergence between the in-context and on-disk versions that step-07 will use for artifact generation.
81
+ **Description Guard Protocol:** This invocation may modify SKILL.md (especially when `fixed[]` is non-empty). Wrap the `skill-check check --fix` call in the four-phase guard defined in §0 by invoking `{descriptionGuardHelper}` at the capture and verify-restore points:
82
+
83
+ ```bash
84
+ # Phase 1 — capture before the tool call
85
+ uv run {descriptionGuardHelper} capture <staging-skill-dir>/SKILL.md
86
+ # stash the returned `description` as `guarded_description` in workflow context
87
+
88
+ # Phase 2 — run skill-check (see command block above)
89
+
90
+ # Phases 3+4 — verify and restore after the tool call
91
+ uv run {descriptionGuardHelper} verify-restore <staging-skill-dir>/SKILL.md \
92
+ --captured-description "{guarded_description}"
93
+ ```
94
+
95
+ If `restored: true` in the verify-restore output, apply §0's post-restore re-validation hook. If `fixed[]` was non-empty in the skill-check output, also re-read the modified SKILL.md to sync the in-context copy before proceeding — this prevents silent divergence between the in-context and on-disk versions that step 7 will use for artifact generation.
90
96
 
91
97
  **Note:** `skill-check` may return non-zero exit code even when `errorCount` is 0. Always rely on parsed JSON, not the shell exit code.
92
98
 
@@ -113,7 +119,21 @@ If fails: auto-fix (deterministic), re-validate once, record result. If passes:
113
119
 
114
120
  **If step 2 reported `body.max_lines` failure:**
115
121
 
116
- **Description Guard Protocol:** Split operations may rewrite the frontmatter. Wrap the split invocation in the four-phase protocol defined in §0 to capture `guarded_description` before the call, execute, verify, and restore on divergence.
122
+ **Description Guard Protocol:** Split operations may rewrite the frontmatter. Wrap the split invocation in the four-phase guard defined in §0:
123
+
124
+ ```bash
125
+ # Phase 1 — capture before the split
126
+ uv run {descriptionGuardHelper} capture <staging-skill-dir>/SKILL.md
127
+ # stash returned `description` as `guarded_description`
128
+
129
+ # Phase 2 — run the split (selective extraction or, last-resort, split-body --write)
130
+
131
+ # Phases 3+4 — verify and restore after the split
132
+ uv run {descriptionGuardHelper} verify-restore <staging-skill-dir>/SKILL.md \
133
+ --captured-description "{guarded_description}"
134
+ ```
135
+
136
+ If `restored: true` in the verify-restore output, apply §0's post-restore re-validation hook.
117
137
 
118
138
  **Mandatory approach — selective split:** Identify Tier 2 sections by their `## Full` heading prefix (e.g., `## Full API Reference`, `## Full Type Definitions`, `## Full Integration Patterns`). Extract ONLY those sections to `references/`, starting with the largest. Keep ALL Tier 1 content and any smaller sections inline. Inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval (per Vercel research).
119
139
 
@@ -162,7 +182,7 @@ Parse output for: `description_score`, `content_score`, `review_score`, `validat
162
182
  **Apply dismissal rules** in this order:
163
183
 
164
184
  1. **Check score thresholds** against the "Score Thresholds" table in `{tesslDismissalData}`. Most importantly:
165
- - If `description_score < 100`: follow the **recover-then-halt** path defined by the `description-xml-tags-guarded-upstream` rule in `{tesslDismissalData}`. Re-apply step-05 §2a's `<`/`>` → `{`/`}` substitution in place on the staging SKILL.md frontmatter `description`, re-sync the in-context copy, and re-run `npx -y tessl skill review <staging-skill-dir>` once. **Re-run gate:** treat `description_score == 100` on the re-run as the only successful recovery outcome. ANY value strictly less than 100 on the re-run — including 99, intermediate-but-improved scores like 95 (formerly tolerated as "close enough"), or 0 — counts as recovery failure: halt with the original `description-xml-tags-guarded-upstream` failure message from `{tesslDismissalData}`, do NOT proceed to §6b, and do NOT downgrade the recovery to a warning. If the re-run produces `description_score == 100`, log `description-recovery: applied ({count} substitutions)` in the evidence report under "Dismissed tessl suggestions" and continue suggestion iteration against the rerun's `judge_suggestions[]`.
185
+ - If `description_score < 100`: follow the **recover-then-halt** path defined by the `description-xml-tags-guarded-upstream` rule in `{tesslDismissalData}`. Re-apply step 5 §2a's `<`/`>` → `{`/`}` substitution in place on the staging SKILL.md frontmatter `description`, re-sync the in-context copy, and re-run `npx -y tessl skill review <staging-skill-dir>` once. **Re-run gate:** treat `description_score == 100` on the re-run as the only successful recovery outcome. ANY value strictly less than 100 on the re-run — including 99, intermediate-but-improved scores like 95 (formerly tolerated as "close enough"), or 0 — counts as recovery failure: halt with the original `description-xml-tags-guarded-upstream` failure message from `{tesslDismissalData}`, do NOT proceed to §6b, and do NOT downgrade the recovery to a warning. If the re-run produces `description_score == 100`, log `description-recovery: applied ({count} substitutions)` in the evidence report under "Dismissed tessl suggestions" and continue suggestion iteration against the rerun's `judge_suggestions[]`.
166
186
  - If `review_score < 60` or `content_score < 60`: record warnings in the evidence report, continue.
167
187
  2. **Iterate `judge_suggestions[]`.** For each suggestion:
168
188
  - Cross-reference against the rules in `{tesslDismissalData}` in order.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: skf-create-stack-skill
3
- description: Consolidated project stack skill with integration patterns — code-mode (analyzes manifests) or compose-mode (synthesizes from existing skills + architecture doc). Use when the user requests to "create a stack skill."
3
+ description: Consolidated project stack skill with integration patterns — code-mode (analyzes manifests) or compose-mode (synthesizes from existing skills + architecture doc). Use when the user requests to "create a stack skill", "forge a stack", or "stack this project".
4
4
  ---
5
5
 
6
6
  # Create Stack Skill
@@ -9,46 +9,80 @@ description: Consolidated project stack skill with integration patterns — code
9
9
 
10
10
  Produces a consolidated stack skill documenting how libraries connect. **Code-mode** analyzes dependency manifests and co-import patterns from actual source code. **Compose-mode** synthesizes from pre-generated individual skills and architecture documents when no codebase exists yet. Every finding must trace to actual code with file:line citations; in compose-mode, inferred integrations are permitted but must be labeled `[inferred from shared domain]`.
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
+
12
20
  ## Role
13
21
 
14
- You are a dependency analyst and integration architect operating in Ferris Architect mode. You bring expertise in dependency analysis, cross-library integration patterns, and compositional architecture, while the user brings their project knowledge and scope preferences.
22
+ You are a dependency analyst and integration architect. You bring expertise in dependency analysis, cross-library integration patterns, and compositional architecture, while the user brings their project knowledge and scope preferences.
15
23
 
16
24
  ## Workflow Rules
17
25
 
18
26
  These rules apply to every step in this workflow:
19
27
 
20
28
  - Zero hallucination — all extracted content must trace to actual source code (compose-mode inferences must be labeled)
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
29
  - Only load one step file at a time — never preload future steps
24
30
  - If any instruction references a subprocess or tool you lack, achieve the outcome in your main context thread
25
31
  - Always communicate in `{communication_language}`
26
32
  - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
27
- - **Workflow state contract `workflow_warnings[]` accumulator (M4):** every step that emits a warning ("log a warning", "record in workflow state for the evidence report", "Warning: ...", etc.) MUST append a structured entry to a single in-memory list named `workflow_warnings[]`. Each entry has the shape `{step: "step-NN", severity: "info|warn|error", code: "<short-slug>", message: "<human text>", context: {<optional fields>}}`. Step-07 surfaces these in `evidence-report.md`; step-08 may add validation findings; step-09 §5 reads the accumulated list and renders the user-facing "Warnings" section. Do not invent a per-step warning channel — there is exactly one accumulator for the whole workflow.
33
+ - Warnings use a single accumulator see `## Workflow state contract` below for shape and surfacing.
34
+
35
+ ## Workflow state contract
36
+
37
+ Every step that emits a warning ("log a warning", "record in workflow state for the evidence report", "Warning: ...", etc.) appends a structured entry to a single in-memory list named `workflow_warnings[]`. Each entry has the shape `{step: "step-NN", severity: "info|warn|error", code: "<short-slug>", message: "<human text>", context: {<optional fields>}}`. Step 7 surfaces these in `evidence-report.md`; step 8 may add validation findings; step 9 §5 reads the accumulated list and renders the user-facing "Warnings" section. There is exactly one accumulator for the whole workflow — do not invent per-step channels.
28
38
 
29
39
  ## Stages
30
40
 
31
41
  | # | Step | File | Auto-proceed |
32
42
  |---|------|------|--------------|
33
- | 1 | Initialize & Mode Detection | steps-c/step-01-init.md | No (confirm) |
34
- | 2 | Detect Manifests | steps-c/step-02-detect-manifests.md | Yes |
35
- | 3 | Rank & Confirm Libraries | steps-c/step-03-rank-and-confirm.md | No (confirm) |
36
- | 4 | Parallel Extract | steps-c/step-04-parallel-extract.md | Yes |
37
- | 5 | Detect Integrations | steps-c/step-05-detect-integrations.md | Yes |
38
- | 6 | Compile Stack | steps-c/step-06-compile-stack.md | No (review) |
39
- | 7 | Generate Output | steps-c/step-07-generate-output.md | Yes |
40
- | 8 | Validate | steps-c/step-08-validate.md | Yes |
41
- | 9 | Report | steps-c/step-09-report.md | Yes |
42
- | 10 | Workflow Health Check | steps-c/step-10-health-check.md | Yes |
43
+ | 1 | Initialize & Mode Detection | references/init.md | No (confirm) |
44
+ | 2 | Detect Manifests | references/detect-manifests.md | Yes |
45
+ | 3 | Rank & Confirm Libraries | references/rank-and-confirm.md | No (confirm) |
46
+ | 4 | Parallel Extract | references/parallel-extract.md | Yes |
47
+ | 5 | Detect Integrations | references/detect-integrations.md | Yes |
48
+ | 6 | Compile Stack | references/compile-stack.md | No (review) |
49
+ | 7 | Generate Output | references/generate-output.md | Yes |
50
+ | 8 | Validate | references/validate.md | Yes |
51
+ | 9 | Report | references/report.md | Yes |
52
+ | 10 | Workflow Health Check | references/health-check.md | Yes |
43
53
 
44
54
  ## Invocation Contract
45
55
 
46
56
  | Aspect | Detail |
47
57
  |--------|--------|
48
58
  | **Inputs** | project_path [required], mode (code/compose) [auto-detected] |
49
- | **Gates** | step-03: Confirm Gate [C] | step-06: Review Gate [C] |
59
+ | **Gates** | step 3: Confirm Gate [C] | step 6: Review Gate [C] |
50
60
  | **Outputs** | SKILL.md (stack), context-snippet.md, metadata.json |
51
61
  | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true |
62
+ | **Exit codes** | See "Exit Codes" below |
63
+
64
+ ## Exit Codes
65
+
66
+ Every HARD HALT in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
67
+
68
+ | Code | Meaning | Raised by |
69
+ | ---- | -------------------- | ------------------------------------------------------------------------------------------ |
70
+ | 0 | success | step 10 (terminal handoff to shared health-check) |
71
+ | 2 | input-missing / input-invalid | step 1 §0 (`config.yaml` missing or malformed); step 2 §2 headless cannot proceed without manifests (S2); step 4 §3 all extractions failed (B7); step 5 §2 feasibility-report `schemaVersion` mismatch; compose-mode-rules `schemaVersion` mismatch |
72
+ | 3 | resolution-failure | step 1 §1 (`forge-tier.yaml` missing); step 2 §0 compose-mode skill resolution corruption (manifest + symlink both fail); step 2 §0 compose-mode zero qualifying skills (S1/S3) |
73
+ | 4 | write-failure | step 7 §1 stage-dir / commit-dir failure; step 7 §1 group-dir collision when an existing non-stack skill occupies the target path |
74
+ | 5 | overwrite-cancelled | step 7 collision when the user declines to replace an existing stack package |
75
+ | 6 | user-cancelled | any interactive menu in step 3 / step 6 (user selected `[X]` Cancel and exit) |
76
+
77
+ ## Result Contract (Headless)
78
+
79
+ When `{headless_mode}` is true, step 9 emits a single-line JSON envelope on **stdout** before chaining to step 10, and every HARD HALT emits the same envelope shape on **stderr** with `status: "error"`:
80
+
81
+ ```
82
+ SKF_STACK_RESULT_JSON: {"status":"success|error","skill_package":"…|null","skill_name":"…","stack_libraries":["…"],"mode":"code|compose","exit_code":0,"halt_reason":null}
83
+ ```
84
+
85
+ `status` is `"success"` on the terminal happy path, `"error"` on any HALT. `skill_package` is the absolute path to the committed stack-skill directory (or `null` on error before commit). `skill_name` is the stack skill's published name (e.g. `{project_name}-stack`). `stack_libraries` is the array of library names included in the stack (constituent skill names in compose-mode, dependency names in code-mode). `mode` is `"code"` or `"compose"` per the run's resolved mode. `halt_reason` is one of: `null` (success), `"input-missing"`, `"input-invalid"`, `"forge-tier-missing"`, `"config-missing"`, `"no-manifests"`, `"all-extractions-failed"`, `"schema-version-mismatch"`, `"resolution-failure"`, `"write-failure"`, `"overwrite-cancelled"`, `"user-cancelled"`. `exit_code` matches the table above.
52
86
 
53
87
  ## On Activation
54
88
 
@@ -61,4 +95,29 @@ These rules apply to every step in this workflow:
61
95
  3. **Preferences fallback.** Otherwise, read `headless_mode` from `{sidecar_path}/preferences.yaml` (`true` or `false`).
62
96
  4. **Default:** `false`.
63
97
 
64
- 3. Load, read the full file, and then execute `./steps-c/step-01-init.md` to begin the workflow.
98
+ 3. **Resolve workflow customization.** Run:
99
+
100
+ ```bash
101
+ python3 {project-root}/_bmad/scripts/resolve_customization.py \
102
+ --skill {skill-root} --key workflow
103
+ ```
104
+
105
+ The script merges the three customization layers per `bmad-customize`'s structural merge rules (scalars override, arrays append):
106
+
107
+ - `{skill-root}/customize.toml` — bundled defaults
108
+ - `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
109
+ - `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
110
+
111
+ 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.
112
+
113
+ Apply the path-scalar fallback now so stage files don't have to repeat the conditional logic. For each of the five scalars, if the merged value is empty or absent, use the bundled default:
114
+
115
+ - `{stackSkillTemplatePath}` ← `workflow.stack_skill_template_path` if non-empty, else `assets/stack-skill-template.md`
116
+ - `{integrationPatternsPath}` ← `workflow.integration_patterns_path` if non-empty, else `references/integration-patterns.md`
117
+ - `{manifestPatternsPath}` ← `workflow.manifest_patterns_path` if non-empty, else `references/manifest-patterns.md`
118
+ - `{composeModeRulesPath}` ← `workflow.compose_mode_rules_path` if non-empty, else `references/compose-mode-rules.md`
119
+ - `{provenanceMapSchemaPath}` ← `workflow.provenance_map_schema_path` if non-empty, else `assets/provenance-map-schema.md`
120
+
121
+ Stash all five as workflow-context variables. Stage files reference `{stackSkillTemplatePath}` / `{integrationPatternsPath}` / `{manifestPatternsPath}` / `{composeModeRulesPath}` / `{provenanceMapSchemaPath}` 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.
122
+
123
+ 4. Load, read the full file, and then execute `references/init.md` to begin the workflow.
@@ -0,0 +1,102 @@
1
+ ---
2
+ type: static-reference
3
+ ---
4
+
5
+ # provenance-map.json Schema
6
+
7
+ Canonical schema templates for the workspace `provenance-map.json` artifact written in step 7 §7. Two variants exist — choose by the run's resolved mode:
8
+
9
+ - **code-mode** — when the workflow analyzed a real codebase with manifest files and AST-extracted exports
10
+ - **compose-mode** — when the workflow synthesized a stack from pre-generated constituent skills + an architecture document
11
+
12
+ Both variants share the top-level `provenance_version`, `skill_name`, `skill_type`, `generated_at`, `entries[]`, and `integrations[]` shape. They differ in source-anchor fields (`source_repo` / `source_commit` / `source_ref`), in `entries[].extraction_method` values, in `integrations[].detection_method` values, and in compose-mode's additional `constituents[]` array which enables drift detection via metadata-hash comparison.
13
+
14
+ > **Note:** Per-export entries use the same schema as single skills (see `skill-sections.md`), with `source_library` identifying the originating library. In compose-mode, `constituents[]` enables audit to detect constituent drift via metadata hash comparison.
15
+
16
+ ## Code-mode variant
17
+
18
+ Used when the workflow ran in code-mode against an actual codebase. `source_repo` and `source_commit` capture the upstream anchor(s); `entries[].extraction_method` records how each export was discovered (`ast_bridge`, `source_reading`, or `qmd_bridge`). `integrations[].detection_method` is `"co-import grep"` because integration pairs are confirmed by co-import file evidence.
19
+
20
+ ```json
21
+ {
22
+ "provenance_version": "2.0",
23
+ "skill_name": "{project_name}-stack",
24
+ "skill_type": "stack",
25
+ "source_repo": ["{repo_url_1}", "{repo_url_2}"],
26
+ "source_commit": {"{repo_1}": "{hash_1}", "{repo_2}": "{hash_2}"},
27
+ "generated_at": "{ISO-8601}",
28
+ "entries": [
29
+ {
30
+ "export_name": "{name}",
31
+ "export_type": "{type}",
32
+ "source_library": "{library-name}",
33
+ "params": [],
34
+ "return_type": "{type}",
35
+ "source_file": "{file}",
36
+ "source_line": 0,
37
+ "confidence": "T1|T1-low|T2",
38
+ "extraction_method": "ast_bridge|source_reading|qmd_bridge",
39
+ "signature_source": "T1|T2|T3"
40
+ }
41
+ ],
42
+ "integrations": [
43
+ {
44
+ "libraries": ["{libA}", "{libB}"],
45
+ "pattern_type": "{type}",
46
+ "detection_method": "co-import grep",
47
+ "co_import_files": [{"file": "{path}", "line": 0}],
48
+ "confidence": "T1|T2"
49
+ }
50
+ ]
51
+ }
52
+ ```
53
+
54
+ ## Compose-mode variant
55
+
56
+ Used when the workflow ran in compose-mode against pre-generated constituent skills. Source-anchor fields (`source_repo`, `source_commit`, `source_ref`) are `null` because there is no codebase to anchor against — provenance traces back to the constituent skills instead, captured in the `constituents[]` array. Each entry's `extraction_method` is `"compose-from-skill"`; integrations have `detection_method` of `"architecture_co_mention"` (named in the architecture doc) or `"inferred_from_shared_domain"` (synthesized inference, must carry T2/T3 confidence).
57
+
58
+ ```json
59
+ {
60
+ "provenance_version": "2.0",
61
+ "skill_name": "{project_name}-stack",
62
+ "skill_type": "stack",
63
+ "source_repo": null,
64
+ "source_commit": null,
65
+ "source_ref": null,
66
+ "generated_at": "{ISO-8601}",
67
+ "entries": [
68
+ {
69
+ "export_name": "{name}",
70
+ "export_type": "{type}",
71
+ "source_library": "{library-name}",
72
+ "params": [],
73
+ "return_type": "{type}",
74
+ "source_file": "{from constituent skill}",
75
+ "source_line": 0,
76
+ "confidence": "T1|T1-low|T2",
77
+ "extraction_method": "compose-from-skill",
78
+ "signature_source": "T1|T2|T3"
79
+ }
80
+ ],
81
+ "integrations": [
82
+ {
83
+ "libraries": ["{libA}", "{libB}"],
84
+ "pattern_type": "{type}",
85
+ "detection_method": "architecture_co_mention|inferred_from_shared_domain",
86
+ "co_import_files": [],
87
+ "confidence": "T2|T3"
88
+ }
89
+ ],
90
+ "constituents": [
91
+ {
92
+ "skill_name": "{constituent-skill-name}",
93
+ "skill_path": "skills/{skill-dir}/",
94
+ "version": "{version from constituent metadata.json}",
95
+ "composed_at": "{ISO-8601}",
96
+ "metadata_hash": "sha256:{hash of constituent metadata.json}"
97
+ }
98
+ ]
99
+ }
100
+ ```
101
+
102
+ **Use the `metadata_hash` value already stored in workflow state during step 2 (S13) — do NOT re-read and re-hash at step 7 time. The stored hash captures the state as it was at manifest-detection time, which is the correct provenance anchor.**
@@ -32,7 +32,7 @@ description: >
32
32
 
33
33
  | Library | Imports | Key Exports | Confidence | Reference |
34
34
  |---------|---------|-------------|------------|-----------|
35
- | {name} | {count} | {top_exports} | {tier} | [ref](./references/{name}.md) |
35
+ | {name} | {count} | {top_exports} | {tier} | [ref](references/{name}.md) |
36
36
 
37
37
  ## Per-Library Summaries
38
38