bmad-module-skill-forge 2.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +49 -49
  3. package/docs/404.md +10 -8
  4. package/docs/_data/pinned.yaml +22 -13
  5. package/docs/_internal/STABILITY.md +58 -35
  6. package/docs/agents.md +39 -53
  7. package/docs/architecture.md +65 -51
  8. package/docs/bmad-synergy.md +53 -51
  9. package/docs/campaign.md +127 -75
  10. package/docs/concepts.md +34 -28
  11. package/docs/examples.md +70 -68
  12. package/docs/forge-auto.md +34 -31
  13. package/docs/getting-started.md +118 -57
  14. package/docs/how-it-works.md +25 -21
  15. package/docs/index.md +15 -15
  16. package/docs/skill-model.md +70 -59
  17. package/docs/troubleshooting.md +143 -18
  18. package/docs/verifying-a-skill.md +87 -57
  19. package/docs/why-skf.md +24 -22
  20. package/docs/workflows.md +274 -110
  21. package/package.json +18 -10
  22. package/src/forger/preferences.yaml +8 -1
  23. package/src/knowledge/agentskills-spec.md +2 -0
  24. package/src/knowledge/ccc-bridge.md +40 -24
  25. package/src/knowledge/confidence-tiers.md +9 -5
  26. package/src/knowledge/overview.md +3 -3
  27. package/src/knowledge/progressive-capability.md +8 -8
  28. package/src/knowledge/provenance-tracking.md +9 -5
  29. package/src/knowledge/qmd-registry.md +3 -3
  30. package/src/knowledge/skf-knowledge-index.csv +2 -2
  31. package/src/knowledge/skill-lifecycle.md +18 -11
  32. package/src/knowledge/split-body-strategy.md +0 -3
  33. package/src/knowledge/tool-resolution.md +4 -2
  34. package/src/knowledge/version-paths.md +101 -15
  35. package/src/module-help.csv +3 -3
  36. package/src/module.yaml +10 -0
  37. package/src/shared/data/ast-grep-recipes.yaml +608 -0
  38. package/src/shared/data/ide-context-files.json +30 -0
  39. package/src/shared/health-check.md +31 -59
  40. package/src/shared/references/description-guard-protocol.md +12 -9
  41. package/src/shared/references/feasibility-report-schema.md +19 -13
  42. package/src/shared/references/headless-gate-convention.md +55 -3
  43. package/src/shared/references/output-contract-schema.md +19 -8
  44. package/src/shared/references/pipeline-contracts.md +94 -29
  45. package/src/shared/references/tessl-review.md +77 -0
  46. package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
  47. package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
  48. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
  49. package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
  50. package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
  51. package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
  52. package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
  53. package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
  54. package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
  55. package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
  56. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
  57. package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
  58. package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
  59. package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
  60. package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
  61. package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
  62. package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
  63. package/src/shared/scripts/skf-atomic-write.py +233 -64
  64. package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
  65. package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
  66. package/src/shared/scripts/skf-check-unit-records.py +184 -0
  67. package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
  68. package/src/shared/scripts/skf-classify-changed-files.py +772 -0
  69. package/src/shared/scripts/skf-comention-pairs.py +1333 -123
  70. package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
  71. package/src/shared/scripts/skf-count-imports.py +1867 -0
  72. package/src/shared/scripts/skf-count-tokens.py +1 -1
  73. package/src/shared/scripts/skf-derive-assembly-shape.py +21 -3
  74. package/src/shared/scripts/skf-description-guard.py +99 -11
  75. package/src/shared/scripts/skf-detect-docs.py +694 -2
  76. package/src/shared/scripts/skf-detect-language.py +486 -107
  77. package/src/shared/scripts/skf-detect-registry.py +887 -0
  78. package/src/shared/scripts/skf-detect-scripts-assets.py +197 -55
  79. package/src/shared/scripts/skf-detect-tools.py +209 -27
  80. package/src/shared/scripts/skf-detect-workspaces.py +506 -30
  81. package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
  82. package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
  83. package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
  84. package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
  85. package/src/shared/scripts/skf-extract-public-api.py +5247 -96
  86. package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
  87. package/src/shared/scripts/skf-extraction-snapshot.py +664 -0
  88. package/src/shared/scripts/skf-fetch-temporal.py +674 -0
  89. package/src/shared/scripts/skf-find-cycles.py +97 -29
  90. package/src/shared/scripts/skf-find-test-report.py +476 -0
  91. package/src/shared/scripts/skf-forge-tier-rw.py +469 -205
  92. package/src/shared/scripts/skf-github-fetch.py +365 -0
  93. package/src/shared/scripts/skf-github-probe.py +700 -0
  94. package/src/shared/scripts/skf-hash-content.py +180 -20
  95. package/src/shared/scripts/skf-language-corpora.py +1 -1
  96. package/src/shared/scripts/skf-load-provenance.py +161 -27
  97. package/src/shared/scripts/skf-manifest-ops.py +1 -1
  98. package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
  99. package/src/shared/scripts/skf-merge-doc-urls.py +143 -20
  100. package/src/shared/scripts/skf-names-present.py +221 -0
  101. package/src/shared/scripts/skf-pair-intersect.py +119 -28
  102. package/src/shared/scripts/skf-parse-gaps.py +1384 -0
  103. package/src/shared/scripts/skf-preapply.py +1 -1
  104. package/src/shared/scripts/skf-preflight.py +176 -28
  105. package/src/shared/scripts/skf-promote-staged.py +430 -0
  106. package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
  107. package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
  108. package/src/shared/scripts/skf-quick-batch.py +774 -0
  109. package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
  110. package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
  111. package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
  112. package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
  113. package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
  114. package/src/shared/scripts/skf-resolve-authoritative-files.py +155 -14
  115. package/src/shared/scripts/skf-resolve-package.py +523 -106
  116. package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
  117. package/src/shared/scripts/skf-run-lock.py +577 -0
  118. package/src/shared/scripts/skf-scan-manifests.py +998 -57
  119. package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
  120. package/src/shared/scripts/skf-severity-classify.py +712 -108
  121. package/src/shared/scripts/skf-shape-detect.py +298 -204
  122. package/src/shared/scripts/skf-shard-body.py +9 -8
  123. package/src/shared/scripts/skf-skill-inventory.py +2412 -89
  124. package/src/shared/scripts/skf-skills-module.py +472 -0
  125. package/src/shared/scripts/skf-source-tree.py +1796 -0
  126. package/src/shared/scripts/skf-structural-diff.py +787 -121
  127. package/src/shared/scripts/skf-tessl-review.py +706 -0
  128. package/src/shared/scripts/skf-update-active-symlink.py +120 -67
  129. package/src/shared/scripts/skf-update-run-state.py +624 -0
  130. package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
  131. package/src/shared/scripts/skf-validate-brief-schema.py +23 -2
  132. package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
  133. package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
  134. package/src/shared/scripts/skf-validate-output.py +712 -64
  135. package/src/shared/scripts/skf-validate-pins.py +1 -1
  136. package/src/shared/scripts/skf-verify-no-trace.py +116 -10
  137. package/src/shared/scripts/skf-verify-provenance-completeness.py +3945 -152
  138. package/src/shared/scripts/skf-write-skill-brief.py +406 -25
  139. package/src/shared/tool-requirements.yaml +201 -0
  140. package/src/skf-analyze-source/SKILL.md +41 -36
  141. package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
  142. package/src/skf-analyze-source/customize.toml +36 -24
  143. package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
  144. package/src/skf-analyze-source/references/continue.md +2 -13
  145. package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
  146. package/src/skf-analyze-source/references/generate-briefs.md +121 -55
  147. package/src/skf-analyze-source/references/headless-contract.md +42 -15
  148. package/src/skf-analyze-source/references/identify-units.md +96 -110
  149. package/src/skf-analyze-source/references/init.md +73 -71
  150. package/src/skf-analyze-source/references/map-and-detect.md +123 -105
  151. package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
  152. package/src/skf-analyze-source/references/recommend.md +51 -70
  153. package/src/skf-analyze-source/references/scan-project.md +60 -84
  154. package/src/skf-analyze-source/references/scan-root.md +39 -0
  155. package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
  156. package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
  157. package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
  158. package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
  159. package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
  160. package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
  161. package/src/skf-audit-skill/SKILL.md +43 -51
  162. package/src/skf-audit-skill/assets/drift-report-template.md +28 -3
  163. package/src/skf-audit-skill/customize.toml +41 -20
  164. package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
  165. package/src/skf-audit-skill/references/doc-drift.md +141 -0
  166. package/src/skf-audit-skill/references/headless-contract.md +29 -0
  167. package/src/skf-audit-skill/references/health-check.md +2 -0
  168. package/src/skf-audit-skill/references/init.md +149 -210
  169. package/src/skf-audit-skill/references/re-index.md +103 -110
  170. package/src/skf-audit-skill/references/report.md +96 -33
  171. package/src/skf-audit-skill/references/semantic-diff.md +34 -25
  172. package/src/skf-audit-skill/references/severity-classify.md +79 -65
  173. package/src/skf-audit-skill/references/severity-rules.md +15 -6
  174. package/src/skf-audit-skill/references/structural-diff.md +80 -90
  175. package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
  176. package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
  177. package/src/skf-brief-skill/SKILL.md +37 -26
  178. package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
  179. package/src/skf-brief-skill/assets/scope-templates.md +18 -25
  180. package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
  181. package/src/skf-brief-skill/customize.toml +48 -20
  182. package/src/skf-brief-skill/references/analyze-target.md +144 -127
  183. package/src/skf-brief-skill/references/confirm-brief.md +11 -9
  184. package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
  185. package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
  186. package/src/skf-brief-skill/references/gather-intent.md +87 -182
  187. package/src/skf-brief-skill/references/headless-args.md +84 -16
  188. package/src/skf-brief-skill/references/health-check.md +1 -1
  189. package/src/skf-brief-skill/references/invocation-contract.md +18 -13
  190. package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
  191. package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
  192. package/src/skf-brief-skill/references/scope-definition.md +123 -47
  193. package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
  194. package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
  195. package/src/skf-brief-skill/references/version-resolution.md +7 -10
  196. package/src/skf-brief-skill/references/write-brief.md +66 -57
  197. package/src/skf-campaign/SKILL.md +31 -34
  198. package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
  199. package/src/skf-campaign/customize.toml +47 -16
  200. package/src/skf-campaign/references/campaign-contracts.md +39 -29
  201. package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
  202. package/src/skf-campaign/references/health-check.md +7 -16
  203. package/src/skf-campaign/references/step-01-setup.md +46 -84
  204. package/src/skf-campaign/references/step-02-strategy.md +31 -30
  205. package/src/skf-campaign/references/step-03-pins.md +26 -18
  206. package/src/skf-campaign/references/step-04-provenance.md +24 -23
  207. package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
  208. package/src/skf-campaign/references/step-06-batch.md +41 -34
  209. package/src/skf-campaign/references/step-07-capstone.md +40 -25
  210. package/src/skf-campaign/references/step-08-verify.md +28 -29
  211. package/src/skf-campaign/references/step-09-refine.md +29 -30
  212. package/src/skf-campaign/references/step-10-export.md +48 -36
  213. package/src/skf-campaign/references/step-11-maintenance.md +31 -34
  214. package/src/skf-campaign/references/step-resume.md +37 -68
  215. package/src/skf-campaign/scripts/campaign-deps.py +57 -18
  216. package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
  217. package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
  218. package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
  219. package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
  220. package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
  221. package/src/skf-campaign/scripts/campaign-report.py +204 -37
  222. package/src/skf-campaign/scripts/campaign-state.py +1128 -0
  223. package/src/skf-campaign/scripts/campaign-status.py +10 -74
  224. package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
  225. package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
  226. package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
  227. package/src/skf-campaign/templates/campaign-report-template.md +5 -1
  228. package/src/skf-campaign/templates/kickoff-template.md +0 -14
  229. package/src/skf-create-skill/SKILL.md +27 -20
  230. package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
  231. package/src/skf-create-skill/assets/skill-sections.md +21 -11
  232. package/src/skf-create-skill/customize.toml +20 -8
  233. package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
  234. package/src/skf-create-skill/references/batch-mode.md +81 -0
  235. package/src/skf-create-skill/references/ccc-index-check.md +27 -0
  236. package/src/skf-create-skill/references/compile.md +119 -70
  237. package/src/skf-create-skill/references/component-extraction.md +113 -91
  238. package/src/skf-create-skill/references/enrich.md +41 -23
  239. package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
  240. package/src/skf-create-skill/references/extract.md +204 -162
  241. package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
  242. package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
  243. package/src/skf-create-skill/references/extraction-patterns.md +588 -201
  244. package/src/skf-create-skill/references/generate-artifacts.md +145 -93
  245. package/src/skf-create-skill/references/health-check.md +1 -1
  246. package/src/skf-create-skill/references/load-brief.md +53 -36
  247. package/src/skf-create-skill/references/relabel-rule.md +12 -0
  248. package/src/skf-create-skill/references/report.md +37 -57
  249. package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
  250. package/src/skf-create-skill/references/step-auto-shard.md +20 -93
  251. package/src/skf-create-skill/references/step-doc-rot.md +44 -60
  252. package/src/skf-create-skill/references/step-doc-sources.md +28 -19
  253. package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
  254. package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
  255. package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
  256. package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
  257. package/src/skf-create-skill/references/validate.md +173 -99
  258. package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
  259. package/src/skf-create-stack-skill/SKILL.md +29 -56
  260. package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
  261. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
  262. package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
  263. package/src/skf-create-stack-skill/customize.toml +23 -12
  264. package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
  265. package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
  266. package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
  267. package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
  268. package/src/skf-create-stack-skill/references/generate-output.md +138 -128
  269. package/src/skf-create-stack-skill/references/init.md +90 -32
  270. package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
  271. package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
  272. package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
  273. package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
  274. package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
  275. package/src/skf-create-stack-skill/references/report.md +39 -33
  276. package/src/skf-create-stack-skill/references/validate.md +20 -39
  277. package/src/skf-drop-skill/SKILL.md +28 -51
  278. package/src/skf-drop-skill/customize.toml +44 -30
  279. package/src/skf-drop-skill/references/execute.md +83 -146
  280. package/src/skf-drop-skill/references/health-check.md +2 -0
  281. package/src/skf-drop-skill/references/invocation-contract.md +64 -0
  282. package/src/skf-drop-skill/references/report.md +46 -30
  283. package/src/skf-drop-skill/references/select.md +122 -124
  284. package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
  285. package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
  286. package/src/skf-export-skill/SKILL.md +40 -62
  287. package/src/skf-export-skill/assets/managed-section-format.md +14 -50
  288. package/src/skf-export-skill/assets/snippet-format.md +3 -1
  289. package/src/skf-export-skill/customize.toml +42 -13
  290. package/src/skf-export-skill/references/generate-snippet.md +54 -46
  291. package/src/skf-export-skill/references/health-check.md +2 -0
  292. package/src/skf-export-skill/references/invocation-contract.md +15 -0
  293. package/src/skf-export-skill/references/load-skill.md +88 -62
  294. package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
  295. package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
  296. package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
  297. package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
  298. package/src/skf-export-skill/references/package.md +15 -73
  299. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
  300. package/src/skf-export-skill/references/result-envelope.md +31 -16
  301. package/src/skf-export-skill/references/summary.md +41 -17
  302. package/src/skf-export-skill/references/token-report.md +4 -12
  303. package/src/skf-export-skill/references/update-context.md +118 -237
  304. package/src/skf-forger/SKILL.md +61 -37
  305. package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
  306. package/src/skf-forger/customize.toml +43 -0
  307. package/src/skf-forger/references/pipeline-mode.md +73 -18
  308. package/src/skf-forger/scripts/forge-status.py +299 -0
  309. package/src/skf-forger/scripts/parse-pipeline.py +322 -54
  310. package/src/skf-forger/scripts/pipeline-gate.py +428 -0
  311. package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
  312. package/src/skf-quick-skill/SKILL.md +54 -53
  313. package/src/skf-quick-skill/assets/skill-template.md +4 -2
  314. package/src/skf-quick-skill/customize.toml +51 -20
  315. package/src/skf-quick-skill/references/batch-mode.md +74 -21
  316. package/src/skf-quick-skill/references/compile.md +30 -31
  317. package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
  318. package/src/skf-quick-skill/references/finalize.md +69 -18
  319. package/src/skf-quick-skill/references/halt-contract.md +41 -29
  320. package/src/skf-quick-skill/references/health-check.md +5 -3
  321. package/src/skf-quick-skill/references/quick-extract.md +114 -45
  322. package/src/skf-quick-skill/references/registry-resolution.md +9 -64
  323. package/src/skf-quick-skill/references/resolve-target.md +116 -66
  324. package/src/skf-quick-skill/references/write-and-validate.md +79 -40
  325. package/src/skf-refine-architecture/SKILL.md +43 -53
  326. package/src/skf-refine-architecture/customize.toml +49 -20
  327. package/src/skf-refine-architecture/references/compile.md +97 -54
  328. package/src/skf-refine-architecture/references/exit-codes.md +30 -0
  329. package/src/skf-refine-architecture/references/finding-storage.md +21 -0
  330. package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
  331. package/src/skf-refine-architecture/references/improvements.md +14 -20
  332. package/src/skf-refine-architecture/references/init.md +98 -57
  333. package/src/skf-refine-architecture/references/issue-detection.md +32 -39
  334. package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
  335. package/src/skf-refine-architecture/references/report.md +65 -54
  336. package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
  337. package/src/skf-rename-skill/SKILL.md +19 -54
  338. package/src/skf-rename-skill/customize.toml +32 -26
  339. package/src/skf-rename-skill/references/execute.md +230 -184
  340. package/src/skf-rename-skill/references/exit-codes.md +7 -7
  341. package/src/skf-rename-skill/references/health-check.md +1 -7
  342. package/src/skf-rename-skill/references/invocation-contract.md +25 -0
  343. package/src/skf-rename-skill/references/report.md +63 -52
  344. package/src/skf-rename-skill/references/select.md +197 -115
  345. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
  346. package/src/skf-setup/SKILL.md +26 -26
  347. package/src/skf-setup/customize.toml +14 -10
  348. package/src/skf-setup/references/auto-index.md +50 -63
  349. package/src/skf-setup/references/ccc-index.md +29 -90
  350. package/src/skf-setup/references/detect-and-tier.md +58 -67
  351. package/src/skf-setup/references/health-check.md +2 -0
  352. package/src/skf-setup/references/invocation-contract.md +14 -0
  353. package/src/skf-setup/references/report.md +43 -166
  354. package/src/skf-setup/references/tier-rules.md +5 -5
  355. package/src/skf-setup/references/write-config.md +34 -57
  356. package/src/skf-test-skill/SKILL.md +17 -57
  357. package/src/skf-test-skill/assets/output-section-formats.md +33 -34
  358. package/src/skf-test-skill/customize.toml +27 -13
  359. package/src/skf-test-skill/references/coherence-check.md +134 -62
  360. package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
  361. package/src/skf-test-skill/references/coverage-check.md +209 -264
  362. package/src/skf-test-skill/references/detect-mode.md +5 -5
  363. package/src/skf-test-skill/references/external-validators.md +94 -82
  364. package/src/skf-test-skill/references/health-check.md +6 -7
  365. package/src/skf-test-skill/references/init.md +164 -91
  366. package/src/skf-test-skill/references/invocation-contract.md +66 -0
  367. package/src/skf-test-skill/references/migration-section-rules.md +5 -2
  368. package/src/skf-test-skill/references/report.md +164 -142
  369. package/src/skf-test-skill/references/score.md +80 -107
  370. package/src/skf-test-skill/references/scoring-rules.md +42 -155
  371. package/src/skf-test-skill/references/source-access-protocol.md +18 -94
  372. package/src/skf-test-skill/references/step-hard-gate.md +51 -33
  373. package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
  374. package/src/skf-test-skill/scripts/build-result-context.py +380 -0
  375. package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
  376. package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
  377. package/src/skf-test-skill/scripts/compute-score.py +223 -45
  378. package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
  379. package/src/skf-test-skill/scripts/hard-gate.py +203 -0
  380. package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
  381. package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
  382. package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
  383. package/src/skf-test-skill/scripts/score-signatures.py +479 -0
  384. package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
  385. package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
  386. package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
  387. package/src/skf-test-skill/templates/test-report-template.md +21 -15
  388. package/src/skf-update-skill/SKILL.md +38 -25
  389. package/src/skf-update-skill/customize.toml +27 -12
  390. package/src/skf-update-skill/references/detect-changes.md +253 -223
  391. package/src/skf-update-skill/references/gap-driven.md +336 -0
  392. package/src/skf-update-skill/references/health-check.md +31 -5
  393. package/src/skf-update-skill/references/init.md +254 -84
  394. package/src/skf-update-skill/references/invocation-contract.md +12 -0
  395. package/src/skf-update-skill/references/manual-section-rules.md +6 -23
  396. package/src/skf-update-skill/references/merge.md +182 -49
  397. package/src/skf-update-skill/references/re-extract.md +53 -262
  398. package/src/skf-update-skill/references/report.md +102 -35
  399. package/src/skf-update-skill/references/write.md +357 -155
  400. package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
  401. package/src/skf-verify-stack/SKILL.md +23 -42
  402. package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
  403. package/src/skf-verify-stack/customize.toml +55 -22
  404. package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
  405. package/src/skf-verify-stack/references/coverage.md +67 -64
  406. package/src/skf-verify-stack/references/exit-codes.md +21 -8
  407. package/src/skf-verify-stack/references/init.md +107 -62
  408. package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
  409. package/src/skf-verify-stack/references/integrations.md +106 -65
  410. package/src/skf-verify-stack/references/report.md +59 -42
  411. package/src/skf-verify-stack/references/requirements.md +32 -9
  412. package/src/skf-verify-stack/references/synthesize.md +53 -39
  413. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
  414. package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
  415. package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
  416. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
  417. package/tools/cli/commands/install.js +11 -4
  418. package/tools/cli/commands/status.js +15 -3
  419. package/tools/cli/commands/update.js +11 -4
  420. package/tools/cli/lib/tool-check.js +320 -0
  421. package/tools/cli/lib/version-check.js +22 -9
  422. package/tools/cli/skf-cli.js +4 -1
  423. package/.gitattributes +0 -16
  424. package/.nvmrc +0 -1
  425. package/docs/_internal/RELEASING.md +0 -679
  426. package/release-audits/v1.0.0-launch-audit.md +0 -728
  427. package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
  428. package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
  429. package/src/skf-campaign/manifest.yaml +0 -15
  430. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
  431. package/src/skf-create-skill/references/ecosystem-check.md +0 -74
  432. package/src/skf-drop-skill/references/headless-contract.md +0 -18
  433. package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
  434. package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
  435. package/src/skf-update-skill/references/validate.md +0 -124
  436. package/tools/validate-docs-drift.js +0 -233
  437. package/tools/validate-docs-links.js +0 -665
@@ -2,6 +2,10 @@
2
2
  stateSchemaFile: 'assets/campaign-state-schema.json'
3
3
  stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
4
4
  backupFile: '{campaignWorkspacePath}/_campaign-state.yaml.bak'
5
+ briefFile: '{campaignWorkspacePath}/campaign-brief.yaml'
6
+ resultContextFile: '{campaignWorkspacePath}/_result-context.json'
7
+ decisionLogFile: '{campaignWorkspacePath}/_campaign-decision-log.md'
8
+ stateScript: 'scripts/campaign-state.py'
5
9
  validateScript: 'scripts/campaign-validate-state.py'
6
10
  statusScript: 'scripts/campaign-status.py'
7
11
  ---
@@ -12,104 +16,69 @@ statusScript: 'scripts/campaign-status.py'
12
16
 
13
17
  ## STEP GOAL:
14
18
 
15
- Validate campaign state integrity, determine the resume point, and chain to the appropriate stage step file. This is a read-only routing step — it does not modify state.
19
+ Validate campaign state integrity, recover it from its backup when it must, get the resume point from the state helper, and chain to that stage's step file.
16
20
 
17
21
  ## RULES
18
22
 
19
- - This step is **read-only by default** — it does NOT modify `{stateFile}` except in the one recovery case below (restoring a valid `.bak` over a corrupt primary), which is the State Contract's advertised safety net.
20
- - Validate state on load via `uv run {validateScript} --state-file {stateFile}`; the recovery path in §1 governs what happens on failure.
21
- - The chain target is determined dynamically from state — there is no fixed `nextStepFile`.
22
- - If `{headless_mode}` is true, auto-proceed through any confirmation gates with the default action and log each auto-decision.
23
+ - Leave state untouched except for the two recovery or reset choices below (§1 backup restore, §3 `[R]e-run`), each through `{stateScript}`, and on a non-zero exit HALT with the same code. A log entry is `uv run {stateScript} log --log-file {decisionLogFile} --type <decision|auto|event> --text '<entry>'`.
24
+ - The chain target is the `step_file` the helper's `resume` operation (`{stateScript}`) returns: never work the stage out by hand.
25
+ - If `{headless_mode}` is true, auto-proceed through any confirmation gates with the default action and log each auto-decision (type `auto`); emit the progress events, and at any HARD HALT the error envelope, per `references/campaign-contracts.md`. A campaign already complete (§3) is no error halt: it ends with the success line.
23
26
 
24
27
  ## TASKS
25
28
 
26
- ### §1 — Read + Validate State (with `.bak` recovery)
29
+ ### §1: Read + Validate State (with `.bak` recovery)
27
30
 
28
- Load `{stateFile}`. If the file does not exist, HALT (exit code 2, `invalid-input`): "No campaign state found. Run `campaign` to start a new campaign."
31
+ If neither `{stateFile}` nor `{backupFile}` exists, HALT (exit code 2, `invalid-input`): "No campaign state found. Run `campaign` to start a new campaign."
29
32
 
30
33
  Run `uv run {validateScript} --state-file {stateFile}`. If it succeeds (exit 0), proceed to §2.
31
34
 
32
- If it fails (primary missing/corrupt YAML/schema-invalid), **attempt automatic recovery from the backup** rather than dead-halting — this is exactly the crash-during-write case the State Contract promises `.bak` covers:
35
+ If it fails (primary missing, corrupt YAML or schema-invalid), recover from the backup rather than dead-halting. `{stateScript}` writes atomically, so its writes never leave a broken primary: the cause is a hand edit, disk damage, or a state written before v3.0.0.
33
36
 
34
- 1. If `{backupFile}` exists, run `uv run {validateScript} --state-file {backupFile}`.
35
- 2. If the backup is valid: copy `{backupFile}` over `{stateFile}`, log to the decision log "primary corrupt — recovered from backup as of {bak.last_updated}", inform the operator, and continue with the recovered state.
36
- 3. If the backup is missing or also invalid: HALT with exit code 9 (`corrupt-state`), reporting both the primary and backup validation errors so the operator knows neither is usable.
37
-
38
- ### §2 — Backup Consistency Check
39
-
40
- Check if `{backupFile}` exists.
41
-
42
- **If `.bak` does not exist:** warn "No backup file found — campaign may have been created but never modified." Continue.
43
-
44
- **If `.bak` exists** (and §1 did not already recover from it):
45
-
46
- 1. Run `uv run {validateScript} --state-file {backupFile}`. If invalid, warn: "Backup file fails validation — cannot use for recovery." Continue with the primary.
47
- 2. Compare primary vs backup deterministically: run `uv run {statusScript} --state-file {stateFile} --backup-file {backupFile}` and read `backup_comparison.primary_behind` (the script does the ISO-8601 timestamp and `current_stage` compares — do not order the timestamps by hand). If it is `true`, the primary looks behind the backup (possible crash during last write). Present a recovery choice:
48
- - `[R]ecover` — copy `{backupFile}` over `{stateFile}` and resume from the backup's state.
49
- - `[K]eep` — keep the primary as authoritative (the default).
50
-
51
- In headless mode, default to `[K]eep` and log the auto-decision (a behind-backup primary may be intentional; never silently overwrite without a clear corruption signal — that case is handled in §1). Otherwise the primary is authoritative.
37
+ ```
38
+ uv run {stateScript} recover --state-file {stateFile}
39
+ ```
52
40
 
53
- ### §3 — Determine Resume Point
41
+ It copies `{backupFile}` over `{stateFile}` only when the backup validates. On exit 0, log "primary corrupt: recovered from backup as of {last_updated}" (type `event`, with the `last_updated` it returns), inform the operator, and continue with the recovered state. On exit 9 (the backup is missing or invalid too), HALT with exit code 9 (`corrupt-state`), reporting both the primary's validation errors and the recover call's `errors[]`, so the operator knows neither is usable.
54
42
 
55
- Two paths based on whether `--from=<skill>` was provided in the invocation:
43
+ ### §2: Backup Check
56
44
 
57
- **With `--from=<skill>`:**
45
+ Unless §1 recovered from it, check `{backupFile}`. When it does not exist, warn "No backup file found: campaign may have been created but never modified." When `uv run {validateScript} --state-file {backupFile}` fails, warn "Backup file fails validation: cannot use for recovery." Continue with the primary either way: each write copies the primary it replaces to `.bak`, so a valid primary is never older than its backup.
58
46
 
59
- 1. Find the named skill in `skills[]` by `name`.
60
- 2. If not found → HALT (exit code 2, `invalid-input`): "Unknown skill '{name}'. Known skills: {comma-separated list of all skill names from state}."
61
- 3. If the skill's `status` is `"completed"`, `"failed"`, or `"skipped"`, the operator may have meant to re-run it (e.g. it passed with a low score) rather than skip past it. Present a choice:
62
- - `[R]e-run` — reset the named skill to `"pending"` and resume from its stage. (Read-only step caveat: this single status reset follows read-backup-modify-write — back up first.)
63
- - `[N]ext` — find the next skill in `dependency_graph.execution_order` after the named one whose `status` is `"pending"` or `"active"` and resume there. If none found → HALT (exit code 0, campaign already complete): "All remaining skills are complete. Run `campaign` to start a new campaign."
64
- - `[H]alt` — stop without resuming.
47
+ ### §3: Determine Resume Point
65
48
 
66
- In headless mode, default to `[N]ext` and log the auto-decision. Log the chosen action to the decision log.
67
- 4. If an active skill already exists in `skills[]` AND it is a different skill from the `--from` target, warn: "Skill '{active_name}' is currently active — honoring explicit --from override."
68
- 5. Determine the target step file:
69
- - Tier A skill with status `"pending"` or `"active"` → stage 4 (`step-05-skill-loop.md`)
70
- - Tier B skill with status `"pending"` or `"active"` → stage 5 (`step-06-batch.md`)
49
+ Ask the state helper, passing `--from` when the invocation named a skill:
71
50
 
72
- **Without `--from`:**
51
+ ```
52
+ uv run {stateScript} resume --state-file {stateFile} [--from <skill>]
53
+ ```
73
54
 
74
- 1. Scan `skills[]` for any skill with `status == "active"`.
75
- - If found and `tier == "A"` → resume target is stage 4 (`step-05-skill-loop.md`). The skill loop's §4 will skip completed skills until it reaches the active one.
76
- - If found and `tier == "B"` → resume target is stage 5 (`step-06-batch.md`). The batch step processes Tier B skills.
77
- 2. If no active skill → the next stage to run is `campaign.current_stage + 1`, because `current_stage` records the highest **completed** stage (each stage writes its own number only after its work and gates finish, so a mid-stage halt leaves the previous stage's number on disk). **Terminal cap:** if `campaign.current_stage` is `10`, the resolved stage is `10` itself (`step-11-maintenance.md`) — never `11`; the existing terminal-HALT check in §4 governs whether a stage-10 campaign is already done.
78
- 3. Map the resolved stage number to the corresponding step file using the stage table in §4.
55
+ It returns the stage to resume and its `step_file`, with the `reason`: `active-skill` (a skill an interruption left `active` resumes its own stage, with no `+1`: a Tier A skill the skill loop, which skips finished skills until it reaches it; a Tier B skill the batch stage, whose batch script, `campaign-render-batch.py`, selects the interrupted active Tier B skills together with the pending ones, so resume resets no status), `next-stage` (the stage after `current_stage`, the highest completed stage), `terminal-cap` (stage 10 runs again, never a stage 11), `from-skill` or `from-next`. Then:
79
56
 
80
- ### §4 — Resume Routing
57
+ - **`complete: true`:** nothing is left to resume, which is no error halt: stop with exit code 0 and display "Campaign has reached its final stage. All skills have been processed." (after `--next`: "All remaining skills are complete. Run `campaign` to start a new campaign.") In headless mode, end with the campaign's success line instead of an error envelope. When `reason` is `complete` and `{resultContextFile}` exists (step-11 §2 wrote it), run `uv run {emitEnvelopeHelper} emit --workflow skf-campaign < {resultContextFile}` (the shared emitter: Result Contract in `references/campaign-contracts.md`); otherwise give the same command `{"status": "success", "skills_completed": <completed>, "skills_failed": <failed>, "decision_log": "{decisionLogFile}"}` on stdin, with the counts `uv run {statusScript} --state-file {stateFile}` returns. Display the line it prints verbatim as the run's last line, with nothing after it.
58
+ - **`stage` 1 with no `{briefFile}`:** Setup stopped after it wrote the state but before the brief, which every later stage reads: HALT (exit code 8, `missing-brief`): "Setup did not finish. Run `campaign` and choose overwrite to start the campaign again."
59
+ - **Exit 2** (an unknown `--from` skill): HALT (exit code 2, `invalid-input`) with its `error`, which lists the known skills.
60
+ - **`needs_choice: true`:** the `--from` skill is already `completed`, `failed` or `skipped` (`from_status`), and the operator may have meant to re-run it (e.g. it passed with a low score) rather than skip past it. Present a choice:
61
+ - `[R]e-run`: reset it with `uv run {stateScript} set-skill --state-file {stateFile} --skill <skill> --status pending`, then run the `resume` call again: it now resumes that skill's stage.
62
+ - `[N]ext`: run the `resume` call again with `--next`, which resumes at the next pending or active skill after it in `dependency_graph.execution_order`.
63
+ - `[H]alt`: stop without resuming.
81
64
 
82
- Map the resolved stage number to its step file. Both §3 branches feed this table an **already-resolved** stage number: the active-skill branch supplies stage 4 or 5 directly (and BYPASSES the `+1`), while the no-active-skill branch supplies `current_stage + 1` (terminal-capped at 10). Do not apply the `+1` again here.
65
+ **GATE [default: N]**: in headless mode, default to `[N]ext` and log the auto-decision. Log the chosen action (type `decision`).
66
+ - **`active_other`** set: warn "Skill '{active_other}' is currently active: honoring explicit --from override."
83
67
 
84
- | Stage | Step File |
85
- |-------|-----------|
86
- | 0 | step-01-setup.md |
87
- | 1 | step-02-strategy.md |
88
- | 2 | step-03-pins.md |
89
- | 3 | step-04-provenance.md |
90
- | 4 | step-05-skill-loop.md |
91
- | 5 | step-06-batch.md |
92
- | 6 | step-07-capstone.md |
93
- | 7 | step-08-verify.md |
94
- | 8 | step-09-refine.md |
95
- | 9 | step-10-export.md |
96
- | 10 | step-11-maintenance.md |
68
+ ### §4: Resume Routing
97
69
 
98
- Derive the skill counts deterministically — `uv run {statusScript} --state-file {stateFile}` returns `completed`, `total`, and the per-status counts (`pending` / `active` / `failed` / `skipped`); do not hand-count `skills[]`. Display a resume summary before chaining:
70
+ Derive the skill counts deterministically: `uv run {statusScript} --state-file {stateFile}` returns `completed`, `total`, and the per-status counts (`pending` / `active` / `failed` / `skipped`); do not hand-count `skills[]`. Display a resume summary before chaining:
99
71
 
100
72
  ```
101
73
  CAMPAIGN RESUME: {campaign.name}
102
74
 
103
- Resuming from: Stage {stage_number} — {stage_name}
104
- Target skill: {skill_name} (if --from was used, otherwise "auto-detected" or "N/A")
75
+ Resuming from: Stage {stage} ({step_file})
76
+ Target skill: {skill} (if --from was used, otherwise "auto-detected" or "N/A")
105
77
  Skills completed: {completed} / {total}
106
78
  Skills remaining: {pending} pending, {active} active, {failed} failed, {skipped} skipped
107
79
  Last updated: {campaign.last_updated}
108
80
  ```
109
81
 
110
- If `campaign.current_stage` is `10` and all skills have status `"completed"`, `"failed"`, or `"skipped"`:
111
- HALT (exit code 0, campaign already complete): "Campaign has reached its final stage. All skills have been processed."
112
-
113
82
  ## OUTPUT
114
83
 
115
- Chain to the determined step file.
84
+ Chain to the `step_file` §3 returned (a file in `references/`).
@@ -1,5 +1,5 @@
1
1
  # /// script
2
- # requires-python = ">=3.9"
2
+ # requires-python = ">=3.11"
3
3
  # dependencies = ["pyyaml"]
4
4
  # ///
5
5
  """Campaign Deps — dependency computation and enforcement for campaign skills.
@@ -8,6 +8,19 @@ Two modes:
8
8
  --compute: topological sort of all skills from depends_on edges
9
9
  --check: verify a single skill's dependencies are satisfied
10
10
 
11
+ A Tier A skill may not depend on a Tier B skill: Tier B skills are built in
12
+ the batch stage, after the skill loop that builds Tier A skills, so the Tier A
13
+ skill could never pass its dependency gate. --compute reports each such edge
14
+ in `tier_inversions` and exits 1, so the strategy stage rejects the plan
15
+ before any skill is built. That rule and the dangling-reference rule come
16
+ from campaign-parse-manifest.py's dependency_problems, the one statement of
17
+ both, so Setup and Strategy report the same entries.
18
+
19
+ At a blocked skill, --check names the status of each unmet dependency and the
20
+ headless default: `skip` when every unmet dependency failed or was skipped
21
+ (it can never complete in this run), `halt` when one is still pending or
22
+ active (an order the loop cannot follow).
23
+
11
24
  CLI:
12
25
  uv run campaign-deps.py --compute --state-file <path>
13
26
  uv run campaign-deps.py --check --state-file <path> --skill <name>
@@ -16,28 +29,41 @@ CLI:
16
29
  Output (JSON on stdout):
17
30
  --compute:
18
31
  {"execution_order": [...], "circular_deps_detected": bool,
19
- "cycle_participants": [...] | null, "tier_counts": {"A": int, "B": int}}
32
+ "cycle_participants": [...] | null, "tier_counts": {"A": int, "B": int},
33
+ "tier_inversions": [{"skill": "<Tier A>", "depends_on": "<Tier B>"}]}
20
34
 
21
35
  --check:
22
- {"skill": "name", "ready": bool, "unmet_deps": [...], "forced": bool}
36
+ {"skill": "name", "ready": bool, "unmet_deps": [...], "forced": bool,
37
+ "unmet_status": {"<dep>": "<status>"}, "default_action": "skip" | "halt" | null}
23
38
 
24
39
  Exit codes:
25
40
  0 success / ready / force-override
26
- 1 circular deps / unmet deps / dangling reference
41
+ 1 circular deps / tier inversion / unmet deps / dangling reference
27
42
  2 error (missing file, bad YAML)
28
43
  """
29
44
 
30
45
  from __future__ import annotations
31
46
 
32
47
  import argparse
48
+ import importlib.util
33
49
  import json
34
50
  import sys
35
51
  import heapq
36
52
  from pathlib import Path
37
- from typing import Any, Dict, List, Optional
53
+ from typing import Any, Dict, List
38
54
 
39
55
  import yaml
40
56
 
57
+ MANIFEST_SCRIPT = Path(__file__).resolve().with_name("campaign-parse-manifest.py")
58
+
59
+
60
+ def _dependency_problems(skills: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, str]]]:
61
+ """campaign-parse-manifest.py's dependency_problems (one rule for Setup and Strategy)."""
62
+ spec = importlib.util.spec_from_file_location("campaign_parse_manifest", MANIFEST_SCRIPT)
63
+ manifest = importlib.util.module_from_spec(spec)
64
+ spec.loader.exec_module(manifest)
65
+ return manifest.dependency_problems(skills)
66
+
41
67
 
42
68
  def _emit_error(message: str, code: str) -> None:
43
69
  json.dump({"error": message, "code": code}, sys.stderr)
@@ -53,17 +79,6 @@ def _build_skill_map(skills: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
53
79
  return {s["name"]: s for s in skills}
54
80
 
55
81
 
56
- def _validate_deps(
57
- skill_map: Dict[str, Dict[str, Any]],
58
- ) -> Optional[List[str]]:
59
- dangling: List[str] = []
60
- for name, skill in skill_map.items():
61
- for dep in skill.get("depends_on", []) or []:
62
- if dep not in skill_map:
63
- dangling.append(f"{name} depends on unknown skill '{dep}'")
64
- return dangling if dangling else None
65
-
66
-
67
82
  def compute(state_file: str) -> int:
68
83
  path = Path(state_file)
69
84
  if not path.is_file():
@@ -89,7 +104,11 @@ def compute(state_file: str) -> int:
89
104
  if tier in tier_counts:
90
105
  tier_counts[tier] += 1
91
106
 
92
- dangling = _validate_deps(skill_map)
107
+ problems = _dependency_problems(list(skill_map.values()))
108
+ dangling = [
109
+ f"{d['skill']} depends on unknown skill '{d['depends_on']}'"
110
+ for d in problems["dangling_depends_on"]
111
+ ]
93
112
  if dangling:
94
113
  _emit_error(
95
114
  f"Dangling dependency references: {'; '.join(dangling)}",
@@ -97,6 +116,8 @@ def compute(state_file: str) -> int:
97
116
  )
98
117
  return 1
99
118
 
119
+ inversions = problems["tier_inversions"]
120
+
100
121
  in_degree: Dict[str, int] = {name: 0 for name in skill_map}
101
122
  adjacency: Dict[str, List[str]] = {name: [] for name in skill_map}
102
123
 
@@ -133,20 +154,29 @@ def compute(state_file: str) -> int:
133
154
  "circular_deps_detected": True,
134
155
  "cycle_participants": cycle_participants,
135
156
  "tier_counts": tier_counts,
157
+ "tier_inversions": inversions,
136
158
  }
137
159
  json.dump(output, sys.stdout, separators=(",", ":"))
138
160
  sys.stdout.write("\n")
139
161
  return 1
140
162
 
163
+ if inversions:
164
+ _emit_error(
165
+ "Tier A skills depend on Tier B skills, which are built after them: "
166
+ + "; ".join(f"{i['skill']} depends on {i['depends_on']}" for i in inversions),
167
+ "TIER_INVERSION",
168
+ )
169
+
141
170
  output = {
142
171
  "execution_order": execution_order,
143
172
  "circular_deps_detected": False,
144
173
  "cycle_participants": None,
145
174
  "tier_counts": tier_counts,
175
+ "tier_inversions": inversions,
146
176
  }
147
177
  json.dump(output, sys.stdout, separators=(",", ":"))
148
178
  sys.stdout.write("\n")
149
- return 0
179
+ return 1 if inversions else 0
150
180
 
151
181
 
152
182
  def check(state_file: str, skill_name: str, force: bool = False) -> int:
@@ -186,6 +216,13 @@ def check(state_file: str, skill_name: str, force: bool = False) -> int:
186
216
 
187
217
  ready = len(unmet) == 0
188
218
  forced = force and not ready
219
+ unmet_status = {dep: skill_map[dep].get("status") for dep in unmet}
220
+ if ready:
221
+ default_action = None
222
+ elif all(status in ("failed", "skipped") for status in unmet_status.values()):
223
+ default_action = "skip"
224
+ else:
225
+ default_action = "halt"
189
226
 
190
227
  if forced:
191
228
  json.dump(
@@ -199,6 +236,8 @@ def check(state_file: str, skill_name: str, force: bool = False) -> int:
199
236
  "ready": ready,
200
237
  "unmet_deps": unmet,
201
238
  "forced": forced,
239
+ "unmet_status": unmet_status,
240
+ "default_action": default_action,
202
241
  }
203
242
  json.dump(output, sys.stdout, separators=(",", ":"))
204
243
  sys.stdout.write("\n")