bmad-module-skill-forge 2.0.0 → 2.0.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 (235) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.nvmrc +1 -1
  3. package/docs/_data/pinned.yaml +1 -1
  4. package/package.json +2 -2
  5. package/src/knowledge/skill-lifecycle.md +1 -1
  6. package/src/module-help.csv +2 -2
  7. package/src/shared/scripts/skf-comention-pairs.py +350 -0
  8. package/src/shared/scripts/skf-count-tokens.py +222 -0
  9. package/src/shared/scripts/skf-detect-docs.py +232 -1
  10. package/src/shared/scripts/skf-detect-language.py +77 -0
  11. package/src/shared/scripts/skf-detect-tools.py +105 -2
  12. package/src/shared/scripts/skf-enumerate-stack-skills.py +111 -2
  13. package/src/shared/scripts/skf-find-cycles.py +246 -0
  14. package/src/shared/scripts/skf-hash-content.py +487 -2
  15. package/src/shared/scripts/skf-manifest-ops.py +67 -1
  16. package/src/shared/scripts/skf-rebuild-managed-sections.py +180 -0
  17. package/src/shared/scripts/skf-recommend-scope-type.py +1 -2
  18. package/src/shared/scripts/skf-render-metadata-stats.py +499 -0
  19. package/src/shared/scripts/skf-rewrite-skill-name.py +308 -0
  20. package/src/shared/scripts/skf-shard-body.py +461 -0
  21. package/src/shared/scripts/skf-skill-inventory.py +139 -3
  22. package/src/shared/scripts/skf-structural-diff.py +307 -67
  23. package/src/shared/scripts/skf-validate-brief-schema.py +39 -1
  24. package/src/shared/scripts/skf-validate-feasibility-report.py +234 -0
  25. package/src/shared/scripts/skf-validate-frontmatter.py +17 -12
  26. package/src/shared/scripts/skf-validate-output.py +443 -10
  27. package/src/shared/scripts/skf-validate-pins.py +1 -1
  28. package/src/shared/scripts/skf-verify-no-trace.py +214 -0
  29. package/src/shared/scripts/skf-verify-provenance-completeness.py +439 -0
  30. package/src/skf-analyze-source/SKILL.md +17 -32
  31. package/src/skf-analyze-source/customize.toml +2 -0
  32. package/src/skf-analyze-source/references/auto-docs-only.md +116 -0
  33. package/src/skf-analyze-source/references/continue.md +8 -13
  34. package/src/skf-analyze-source/references/generate-briefs.md +28 -12
  35. package/src/skf-analyze-source/references/headless-contract.md +33 -0
  36. package/src/skf-analyze-source/references/health-check.md +1 -2
  37. package/src/skf-analyze-source/references/identify-units.md +25 -36
  38. package/src/skf-analyze-source/references/init.md +12 -25
  39. package/src/skf-analyze-source/references/map-and-detect.md +8 -20
  40. package/src/skf-analyze-source/references/recommend.md +4 -14
  41. package/src/skf-analyze-source/references/scan-project.md +3 -11
  42. package/src/skf-analyze-source/references/step-auto-scope.md +42 -180
  43. package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -2
  44. package/src/skf-audit-skill/SKILL.md +9 -6
  45. package/src/skf-audit-skill/customize.toml +2 -0
  46. package/src/skf-audit-skill/references/init.md +58 -32
  47. package/src/skf-audit-skill/references/re-index.md +15 -34
  48. package/src/skf-audit-skill/references/report.md +11 -58
  49. package/src/skf-audit-skill/references/semantic-diff.md +4 -22
  50. package/src/skf-audit-skill/references/severity-classify.md +53 -87
  51. package/src/skf-audit-skill/references/step-doc-drift.md +49 -19
  52. package/src/skf-audit-skill/references/structural-diff.md +43 -68
  53. package/src/skf-brief-skill/SKILL.md +7 -36
  54. package/src/skf-brief-skill/assets/scope-templates.md +1 -1
  55. package/src/skf-brief-skill/customize.toml +14 -0
  56. package/src/skf-brief-skill/references/analyze-target.md +6 -14
  57. package/src/skf-brief-skill/references/confirm-brief.md +2 -14
  58. package/src/skf-brief-skill/references/draft-checkpoint.md +8 -8
  59. package/src/skf-brief-skill/references/gather-intent.md +19 -22
  60. package/src/skf-brief-skill/references/headless-args.md +1 -1
  61. package/src/skf-brief-skill/references/health-check.md +1 -22
  62. package/src/skf-brief-skill/references/invocation-contract.md +37 -0
  63. package/src/skf-brief-skill/references/scope-definition.md +4 -14
  64. package/src/skf-brief-skill/references/step-auto-brief.md +1 -5
  65. package/src/skf-brief-skill/references/step-auto-validate.md +1 -5
  66. package/src/skf-brief-skill/references/version-resolution.md +1 -1
  67. package/src/skf-brief-skill/references/write-brief.md +17 -10
  68. package/src/skf-campaign/SKILL.md +15 -84
  69. package/src/skf-campaign/customize.toml +2 -0
  70. package/src/skf-campaign/manifest.yaml +4 -0
  71. package/src/skf-campaign/references/campaign-contracts.md +67 -0
  72. package/src/skf-campaign/references/campaign-directive-spec.md +14 -103
  73. package/src/skf-campaign/references/step-01-setup.md +4 -5
  74. package/src/skf-campaign/references/step-02-strategy.md +8 -3
  75. package/src/skf-campaign/references/step-03-pins.md +1 -1
  76. package/src/skf-campaign/references/step-04-provenance.md +1 -1
  77. package/src/skf-campaign/references/step-05-skill-loop.md +1 -1
  78. package/src/skf-campaign/references/step-06-batch.md +7 -6
  79. package/src/skf-campaign/references/step-11-maintenance.md +2 -2
  80. package/src/skf-campaign/references/step-resume.md +9 -8
  81. package/src/skf-campaign/scripts/campaign-deps.py +10 -1
  82. package/src/skf-campaign/scripts/campaign-render-batch.py +199 -0
  83. package/src/skf-campaign/scripts/campaign-status.py +162 -0
  84. package/src/skf-campaign/templates/campaign-brief-template.yaml +1 -1
  85. package/src/skf-create-skill/SKILL.md +14 -3
  86. package/src/skf-create-skill/assets/compile-assembly-rules.md +17 -38
  87. package/src/skf-create-skill/assets/skill-sections.md +9 -100
  88. package/src/skf-create-skill/assets/tessl-dismissal-rules.md +4 -13
  89. package/src/skf-create-skill/customize.toml +13 -0
  90. package/src/skf-create-skill/references/authoritative-files-protocol.md +7 -7
  91. package/src/skf-create-skill/references/compile.md +34 -47
  92. package/src/skf-create-skill/references/component-extraction.md +8 -35
  93. package/src/skf-create-skill/references/ecosystem-check.md +9 -25
  94. package/src/skf-create-skill/references/enrich.md +4 -17
  95. package/src/skf-create-skill/references/extract.md +14 -29
  96. package/src/skf-create-skill/references/extraction-patterns-tracing.md +1 -1
  97. package/src/skf-create-skill/references/extraction-patterns.md +7 -7
  98. package/src/skf-create-skill/references/generate-artifacts.md +9 -23
  99. package/src/skf-create-skill/references/health-check.md +1 -1
  100. package/src/skf-create-skill/references/load-brief.md +16 -18
  101. package/src/skf-create-skill/references/report.md +12 -15
  102. package/src/skf-create-skill/references/source-resolution-protocols.md +3 -3
  103. package/src/skf-create-skill/references/step-auto-shard.md +52 -25
  104. package/src/skf-create-skill/references/step-doc-rot.md +30 -12
  105. package/src/skf-create-skill/references/step-doc-sources.md +2 -2
  106. package/src/skf-create-skill/references/sub/ccc-discover.md +2 -13
  107. package/src/skf-create-skill/references/sub/fetch-docs.md +8 -50
  108. package/src/skf-create-skill/references/sub/fetch-temporal.md +5 -20
  109. package/src/skf-create-skill/references/tier-degradation-rules.md +6 -8
  110. package/src/skf-create-skill/references/validate.md +59 -28
  111. package/src/skf-create-skill/scripts/scan-doc-rot.py +245 -0
  112. package/src/skf-create-stack-skill/SKILL.md +15 -9
  113. package/src/skf-create-stack-skill/assets/provenance-map-schema.md +1 -1
  114. package/src/skf-create-stack-skill/customize.toml +10 -0
  115. package/src/skf-create-stack-skill/references/compile-stack.md +7 -8
  116. package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -13
  117. package/src/skf-create-stack-skill/references/detect-integrations.md +49 -54
  118. package/src/skf-create-stack-skill/references/detect-manifests.md +37 -30
  119. package/src/skf-create-stack-skill/references/generate-output.md +24 -75
  120. package/src/skf-create-stack-skill/references/init.md +9 -81
  121. package/src/skf-create-stack-skill/references/manifest-patterns.md +5 -35
  122. package/src/skf-create-stack-skill/references/parallel-extract.md +12 -27
  123. package/src/skf-create-stack-skill/references/rank-and-confirm.md +6 -6
  124. package/src/skf-create-stack-skill/references/report.md +27 -62
  125. package/src/skf-create-stack-skill/references/validate.md +35 -31
  126. package/src/skf-drop-skill/SKILL.md +20 -23
  127. package/src/skf-drop-skill/customize.toml +10 -0
  128. package/src/skf-drop-skill/references/execute.md +51 -31
  129. package/src/skf-drop-skill/references/headless-contract.md +18 -0
  130. package/src/skf-drop-skill/references/health-check.md +1 -13
  131. package/src/skf-drop-skill/references/report.md +42 -10
  132. package/src/skf-drop-skill/references/select.md +88 -48
  133. package/src/skf-drop-skill/scripts/dir-sizes.py +116 -0
  134. package/src/skf-export-skill/SKILL.md +17 -14
  135. package/src/skf-export-skill/assets/managed-section-format.md +2 -5
  136. package/src/skf-export-skill/customize.toml +10 -6
  137. package/src/skf-export-skill/references/generate-snippet.md +21 -35
  138. package/src/skf-export-skill/references/load-skill.md +44 -42
  139. package/src/skf-export-skill/references/multi-skill-mode.md +1 -1
  140. package/src/skf-export-skill/references/orphan-context-detection.md +11 -3
  141. package/src/skf-export-skill/references/package.md +27 -33
  142. package/src/skf-export-skill/references/preflight-snippet-root-probe.md +24 -5
  143. package/src/skf-export-skill/references/result-envelope.md +28 -0
  144. package/src/skf-export-skill/references/summary.md +11 -5
  145. package/src/skf-export-skill/references/token-report.md +42 -24
  146. package/src/skf-export-skill/references/update-context.md +32 -33
  147. package/src/skf-forger/SKILL.md +27 -39
  148. package/src/skf-forger/references/pipeline-mode.md +34 -0
  149. package/src/skf-forger/scripts/parse-pipeline.py +270 -0
  150. package/src/skf-quick-skill/SKILL.md +10 -60
  151. package/src/skf-quick-skill/customize.toml +9 -0
  152. package/src/skf-quick-skill/references/batch-mode.md +6 -4
  153. package/src/skf-quick-skill/references/compile.md +15 -52
  154. package/src/skf-quick-skill/references/ecosystem-check.md +5 -20
  155. package/src/skf-quick-skill/references/finalize.md +12 -6
  156. package/src/skf-quick-skill/references/halt-contract.md +54 -0
  157. package/src/skf-quick-skill/references/health-check.md +3 -4
  158. package/src/skf-quick-skill/references/quick-extract.md +7 -18
  159. package/src/skf-quick-skill/references/registry-resolution.md +1 -23
  160. package/src/skf-quick-skill/references/resolve-target.md +27 -34
  161. package/src/skf-quick-skill/references/write-and-validate.md +10 -25
  162. package/src/skf-refine-architecture/SKILL.md +14 -7
  163. package/src/skf-refine-architecture/customize.toml +9 -0
  164. package/src/skf-refine-architecture/references/compile.md +9 -7
  165. package/src/skf-refine-architecture/references/gap-analysis.md +21 -50
  166. package/src/skf-refine-architecture/references/health-check.md +1 -1
  167. package/src/skf-refine-architecture/references/improvements.md +9 -25
  168. package/src/skf-refine-architecture/references/init.md +9 -16
  169. package/src/skf-refine-architecture/references/issue-detection.md +10 -25
  170. package/src/skf-refine-architecture/references/refinement-rules.md +4 -16
  171. package/src/skf-refine-architecture/references/report.md +13 -10
  172. package/src/skf-rename-skill/SKILL.md +11 -21
  173. package/src/skf-rename-skill/customize.toml +10 -0
  174. package/src/skf-rename-skill/references/execute.md +107 -78
  175. package/src/skf-rename-skill/references/exit-codes.md +12 -0
  176. package/src/skf-rename-skill/references/report.md +16 -8
  177. package/src/skf-rename-skill/references/select.md +69 -42
  178. package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -0
  179. package/src/skf-setup/SKILL.md +20 -12
  180. package/src/skf-setup/customize.toml +10 -0
  181. package/src/skf-setup/references/auto-index.md +9 -10
  182. package/src/skf-setup/references/ccc-index.md +14 -15
  183. package/src/skf-setup/references/detect-and-tier.md +6 -8
  184. package/src/skf-setup/references/report.md +22 -31
  185. package/src/skf-setup/references/tier-rules.md +2 -32
  186. package/src/skf-setup/references/write-config.md +9 -10
  187. package/src/skf-test-skill/SKILL.md +10 -8
  188. package/src/skf-test-skill/customize.toml +2 -0
  189. package/src/skf-test-skill/references/coherence-check.md +19 -31
  190. package/src/skf-test-skill/references/coverage-check.md +109 -75
  191. package/src/skf-test-skill/references/detect-mode.md +10 -41
  192. package/src/skf-test-skill/references/external-validators.md +11 -19
  193. package/src/skf-test-skill/references/init.md +38 -20
  194. package/src/skf-test-skill/references/migration-section-rules.md +4 -4
  195. package/src/skf-test-skill/references/report.md +60 -40
  196. package/src/skf-test-skill/references/score.md +32 -64
  197. package/src/skf-test-skill/references/scoring-rules.md +6 -62
  198. package/src/skf-test-skill/references/source-access-protocol.md +6 -6
  199. package/src/skf-test-skill/references/step-hard-gate.md +1 -3
  200. package/src/skf-test-skill/scripts/aggregate-coherence.py +276 -0
  201. package/src/skf-test-skill/scripts/check-metadata-coherence.py +359 -0
  202. package/src/skf-test-skill/scripts/combine-external-scores.py +190 -0
  203. package/src/skf-test-skill/scripts/compute-score.py +131 -1
  204. package/src/skf-test-skill/scripts/reconcile-coverage.py +327 -0
  205. package/src/skf-test-skill/scripts/validate-inventory.py +251 -0
  206. package/src/skf-test-skill/scripts/verify-declared-numerator.py +192 -0
  207. package/src/skf-update-skill/SKILL.md +17 -5
  208. package/src/skf-update-skill/customize.toml +14 -0
  209. package/src/skf-update-skill/references/detect-changes.md +19 -37
  210. package/src/skf-update-skill/references/health-check.md +3 -9
  211. package/src/skf-update-skill/references/init.md +39 -32
  212. package/src/skf-update-skill/references/manual-section-rules.md +1 -2
  213. package/src/skf-update-skill/references/merge-conflict-rules.md +1 -52
  214. package/src/skf-update-skill/references/merge.md +13 -57
  215. package/src/skf-update-skill/references/re-extract.md +12 -24
  216. package/src/skf-update-skill/references/report.md +14 -7
  217. package/src/skf-update-skill/references/validate.md +32 -30
  218. package/src/skf-update-skill/references/write.md +86 -51
  219. package/src/skf-update-skill/scripts/skf-new-file-diff.py +167 -0
  220. package/src/skf-verify-stack/SKILL.md +11 -21
  221. package/src/skf-verify-stack/customize.toml +12 -3
  222. package/src/skf-verify-stack/references/coverage-patterns.md +1 -29
  223. package/src/skf-verify-stack/references/coverage.md +21 -10
  224. package/src/skf-verify-stack/references/exit-codes.md +14 -0
  225. package/src/skf-verify-stack/references/health-check.md +1 -1
  226. package/src/skf-verify-stack/references/init.md +20 -25
  227. package/src/skf-verify-stack/references/integration-verification-rules.md +10 -19
  228. package/src/skf-verify-stack/references/integrations.md +44 -49
  229. package/src/skf-verify-stack/references/report.md +28 -34
  230. package/src/skf-verify-stack/references/requirements.md +3 -3
  231. package/src/skf-verify-stack/references/synthesize.md +36 -39
  232. package/src/skf-verify-stack/scripts/skf-coverage-tally.py +196 -0
  233. package/src/skf-verify-stack/scripts/skf-report-delta.py +256 -0
  234. package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +258 -0
  235. package/src/skf-rename-skill/references/rebuild-context.md +0 -110
@@ -15,7 +15,8 @@ CLI:
15
15
 
16
16
  Output (JSON on stdout):
17
17
  --compute:
18
- {"execution_order": [...], "circular_deps_detected": bool, "cycle_participants": [...] | null}
18
+ {"execution_order": [...], "circular_deps_detected": bool,
19
+ "cycle_participants": [...] | null, "tier_counts": {"A": int, "B": int}}
19
20
 
20
21
  --check:
21
22
  {"skill": "name", "ready": bool, "unmet_deps": [...], "forced": bool}
@@ -82,6 +83,12 @@ def compute(state_file: str) -> int:
82
83
 
83
84
  skill_map = _build_skill_map(skills)
84
85
 
86
+ tier_counts = {"A": 0, "B": 0}
87
+ for skill in skill_map.values():
88
+ tier = skill.get("tier")
89
+ if tier in tier_counts:
90
+ tier_counts[tier] += 1
91
+
85
92
  dangling = _validate_deps(skill_map)
86
93
  if dangling:
87
94
  _emit_error(
@@ -125,6 +132,7 @@ def compute(state_file: str) -> int:
125
132
  "execution_order": execution_order,
126
133
  "circular_deps_detected": True,
127
134
  "cycle_participants": cycle_participants,
135
+ "tier_counts": tier_counts,
128
136
  }
129
137
  json.dump(output, sys.stdout, separators=(",", ":"))
130
138
  sys.stdout.write("\n")
@@ -134,6 +142,7 @@ def compute(state_file: str) -> int:
134
142
  "execution_order": execution_order,
135
143
  "circular_deps_detected": False,
136
144
  "cycle_participants": None,
145
+ "tier_counts": tier_counts,
137
146
  }
138
147
  json.dump(output, sys.stdout, separators=(",", ":"))
139
148
  sys.stdout.write("\n")
@@ -0,0 +1,199 @@
1
+ # /// script
2
+ # requires-python = ">=3.9"
3
+ # dependencies = ["pyyaml"]
4
+ # ///
5
+ """Campaign Render Batch — build the QS `--batch` input file for Tier B skills.
6
+
7
+ step-06 batches every Tier B skill through `skf-quick-skill --batch`. The batch
8
+ input file is a machine-consumed cross-tool contract: one target per line in the
9
+ exact single-target shape `skf-quick-skill` parses (see
10
+ src/skf-quick-skill/references/batch-mode.md). Hand-building that file in prose
11
+ is filter + join + reformat-structured-data — the same mechanical, all-or-nothing
12
+ work campaign-parse-manifest.py owns for the inverse direction, where a silent
13
+ format slip corrupts the whole run. This script is the single source of truth for
14
+ the generation side so the line format lives in one place, aligned with the
15
+ consumer's contract, instead of being re-derived in the step prose.
16
+
17
+ What it does:
18
+ - Filters `skills[]` for `tier == "B" && status == "pending"` (skips Tier A and
19
+ any already completed/failed/skipped — resume-safe).
20
+ - Looks up each skill's `repo_url` from the brief's `targets[]` (matched by
21
+ name); repo URLs live only in the brief, never in the state schema.
22
+ - Emits one line per skill in the CONSUMER's single-target shape:
23
+ {repo_url} (pin is null → latest)
24
+ {repo_url}@{pin} (skills[].pin is non-null)
25
+ with optional ` language=<lang>` / ` scope=<path>` modifiers appended when the
26
+ brief target carries a `language_hint`/`language` or `scope_hint`/`scope` hint.
27
+ - No skill-name field, no bare pin token — that would not parse as a target.
28
+
29
+ CLI:
30
+ uv run campaign-render-batch.py --state-file <p> --brief-file <p> [-o <batchFile>]
31
+
32
+ Output:
33
+ - Batch text (one target per line) to {batchFile} via -o, else to stdout.
34
+ - JSON summary {written, count, skipped_non_tierB, skipped_non_pending} to stderr.
35
+
36
+ Exit codes:
37
+ 0 batch file written (0+ targets)
38
+ 2 state file missing / unreadable / bad YAML
39
+ 8 brief missing / unreadable (missing-brief HALT), or a pending Tier B skill
40
+ has no matching brief target with a repo_url (unmatched-target)
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ import argparse
46
+ import json
47
+ import sys
48
+ from pathlib import Path
49
+ from typing import Any, Dict, List, Tuple
50
+
51
+ import yaml
52
+
53
+
54
+ def _err(message: str, code: str, exit_code: int, **extra: Any) -> int:
55
+ payload: Dict[str, Any] = {"error": message, "code": code}
56
+ payload.update(extra)
57
+ json.dump(payload, sys.stderr)
58
+ sys.stderr.write("\n")
59
+ return exit_code
60
+
61
+
62
+ def _target_line(repo_url: str, pin: Any, target: Dict[str, Any]) -> str:
63
+ """Render one batch line in the consumer's single-target shape."""
64
+ line = repo_url
65
+ if pin:
66
+ line += f"@{pin}"
67
+ lang = target.get("language_hint") or target.get("language")
68
+ scope = target.get("scope_hint") or target.get("scope")
69
+ if lang:
70
+ line += f" language={lang}"
71
+ if scope:
72
+ line += f" scope={scope}"
73
+ return line
74
+
75
+
76
+ def build_batch(state: Dict[str, Any], brief: Dict[str, Any]) -> Tuple[List[str], Dict[str, Any]]:
77
+ """Filter Tier B pending skills and render batch lines.
78
+
79
+ Returns (lines, summary). `summary["unmatched"]` lists any pending Tier B
80
+ skill names with no brief target / repo_url; the caller HALTs (exit 8) when
81
+ it is non-empty.
82
+ """
83
+ skills = state.get("skills") or []
84
+ targets: Dict[str, Dict[str, Any]] = {
85
+ t.get("name"): t
86
+ for t in (brief.get("targets") or [])
87
+ if isinstance(t, dict) and t.get("name")
88
+ }
89
+
90
+ lines: List[str] = []
91
+ unmatched: List[str] = []
92
+ skipped_non_tierb = 0
93
+ skipped_non_pending = 0
94
+
95
+ for skill in skills:
96
+ if not isinstance(skill, dict):
97
+ continue
98
+ if skill.get("tier") != "B":
99
+ skipped_non_tierb += 1
100
+ continue
101
+ if skill.get("status") != "pending":
102
+ skipped_non_pending += 1
103
+ continue
104
+
105
+ name = skill.get("name")
106
+ target = targets.get(name)
107
+ repo_url = (target or {}).get("repo_url") or ""
108
+ if not repo_url:
109
+ unmatched.append(name)
110
+ continue
111
+
112
+ lines.append(_target_line(repo_url, skill.get("pin"), target))
113
+
114
+ summary = {
115
+ "count": len(lines),
116
+ "skipped_non_tierB": skipped_non_tierb,
117
+ "skipped_non_pending": skipped_non_pending,
118
+ "unmatched": unmatched,
119
+ }
120
+ return lines, summary
121
+
122
+
123
+ def _load_yaml_mapping(path: str) -> Dict[str, Any]:
124
+ data = yaml.safe_load(Path(path).read_text(encoding="utf-8"))
125
+ if data is None:
126
+ return {}
127
+ if not isinstance(data, dict):
128
+ raise ValueError("top-level document is not a mapping")
129
+ return data
130
+
131
+
132
+ def run(state_file: str, brief_file: str, output: str | None) -> int:
133
+ # State problems are plain file/parse errors (exit 2).
134
+ if not Path(state_file).is_file():
135
+ return _err(f"State file not found: {state_file}", "STATE_NOT_FOUND", 2)
136
+ try:
137
+ state = _load_yaml_mapping(state_file)
138
+ except (yaml.YAMLError, ValueError) as exc:
139
+ return _err(f"Failed to parse state YAML: {exc}", "STATE_PARSE_ERROR", 2)
140
+
141
+ # Brief problems preserve step-06 §4's missing-brief HALT (exit 8): repo_urls
142
+ # live only in the brief, so a missing/unreadable/corrupt brief is fatal here.
143
+ if not Path(brief_file).is_file():
144
+ return _err(f"Brief file not found: {brief_file}", "missing-brief", 8)
145
+ try:
146
+ brief = _load_yaml_mapping(brief_file)
147
+ except OSError as exc:
148
+ return _err(f"Brief unreadable: {exc}", "missing-brief", 8)
149
+ except (yaml.YAMLError, ValueError) as exc:
150
+ return _err(f"Brief unparseable: {exc}", "missing-brief", 8)
151
+
152
+ lines, summary = build_batch(state, brief)
153
+
154
+ unmatched = summary.pop("unmatched")
155
+ if unmatched:
156
+ return _err(
157
+ "No matching brief target (repo_url) for pending Tier B skill(s): "
158
+ + ", ".join(str(n) for n in unmatched),
159
+ "unmatched-target",
160
+ 8,
161
+ skills=unmatched,
162
+ )
163
+
164
+ batch_text = "\n".join(lines)
165
+ if batch_text:
166
+ batch_text += "\n"
167
+
168
+ if output:
169
+ Path(output).write_text(batch_text, encoding="utf-8")
170
+ summary["written"] = output
171
+ else:
172
+ sys.stdout.write(batch_text)
173
+ summary["written"] = "<stdout>"
174
+
175
+ ordered = {
176
+ "written": summary["written"],
177
+ "count": summary["count"],
178
+ "skipped_non_tierB": summary["skipped_non_tierB"],
179
+ "skipped_non_pending": summary["skipped_non_pending"],
180
+ }
181
+ json.dump(ordered, sys.stderr)
182
+ sys.stderr.write("\n")
183
+ return 0
184
+
185
+
186
+ def main(argv: list[str] | None = None) -> int:
187
+ parser = argparse.ArgumentParser(
188
+ prog="campaign-render-batch",
189
+ description="Build the QS --batch input file for pending Tier B skills.",
190
+ )
191
+ parser.add_argument("--state-file", required=True)
192
+ parser.add_argument("--brief-file", required=True)
193
+ parser.add_argument("-o", "--output", dest="output", help="batch file path (default: stdout)")
194
+ args = parser.parse_args(argv)
195
+ return run(args.state_file, args.brief_file, args.output)
196
+
197
+
198
+ if __name__ == "__main__":
199
+ raise SystemExit(main())
@@ -0,0 +1,162 @@
1
+ # /// script
2
+ # requires-python = ">=3.9"
3
+ # dependencies = ["pyyaml"]
4
+ # ///
5
+ """Campaign Status — deterministic state summary + optional backup-drift verdict.
6
+
7
+ Collapses two in-prompt determinism leaks into one helper the LLM calls instead
8
+ of hand-deriving:
9
+
10
+ - `campaign status` and the resume summary block tallied `skills[]` by status
11
+ ("N completed / M total, K pending, ...") in-prompt over a 15+ skill array.
12
+ - the `.bak` recovery path compared `primary` vs `backup` timestamps and stage
13
+ numbers by hand (an ISO-8601 + int compare across two YAML files).
14
+
15
+ Both are mechanical, one-correct-answer computations. This script owns them so
16
+ identical state always yields identical output.
17
+
18
+ CLI:
19
+ uv run campaign-status.py --state-file <path>
20
+ uv run campaign-status.py --state-file <path> --backup-file <path>
21
+
22
+ Output (JSON on stdout):
23
+ {
24
+ "campaign_name": "...",
25
+ "current_stage": 0,
26
+ "last_updated": "...",
27
+ "total": 0, "completed": 0, "pending": 0,
28
+ "active": 0, "failed": 0, "skipped": 0,
29
+ "backup_comparison": null | {
30
+ "primary_behind": bool,
31
+ "primary_last_updated": "...", "backup_last_updated": "...",
32
+ "primary_stage": 0, "backup_stage": 0
33
+ }
34
+ }
35
+
36
+ Exit codes:
37
+ 0 ok
38
+ 2 error (state missing / unreadable / not a mapping)
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ import argparse
44
+ import json
45
+ import sys
46
+ from datetime import datetime
47
+ from pathlib import Path
48
+ from typing import Any, Dict, Optional
49
+
50
+ import yaml
51
+
52
+ STATUSES = ("pending", "active", "completed", "failed", "skipped")
53
+
54
+
55
+ def _err(message: str, code: str) -> int:
56
+ json.dump({"error": message, "code": code}, sys.stderr)
57
+ sys.stderr.write("\n")
58
+ return 2
59
+
60
+
61
+ def _load_state(path: Path) -> Optional[Dict[str, Any]]:
62
+ if not path.is_file():
63
+ return None
64
+ try:
65
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
66
+ except (yaml.YAMLError, OSError):
67
+ return None
68
+ return data if isinstance(data, dict) else None
69
+
70
+
71
+ def _parse_iso(value: Any) -> Optional[datetime]:
72
+ if not isinstance(value, str) or not value:
73
+ return None
74
+ text = value[:-1] + "+00:00" if value.endswith("Z") else value
75
+ try:
76
+ return datetime.fromisoformat(text)
77
+ except (ValueError, TypeError):
78
+ return None
79
+
80
+
81
+ def _counts(state: Dict[str, Any]) -> Dict[str, int]:
82
+ skills = state.get("skills", []) or []
83
+ tally = {s: 0 for s in STATUSES}
84
+ for skill in skills:
85
+ status = skill.get("status")
86
+ if status in tally:
87
+ tally[status] += 1
88
+ tally["total"] = len(skills)
89
+ return tally
90
+
91
+
92
+ def _compare_backup(primary: Dict[str, Any], backup: Dict[str, Any]) -> Dict[str, Any]:
93
+ p_c = primary.get("campaign", {}) or {}
94
+ b_c = backup.get("campaign", {}) or {}
95
+ p_updated = p_c.get("last_updated")
96
+ b_updated = b_c.get("last_updated")
97
+ p_stage = p_c.get("current_stage")
98
+ b_stage = b_c.get("current_stage")
99
+
100
+ stage_behind = (
101
+ isinstance(p_stage, int) and isinstance(b_stage, int) and p_stage < b_stage
102
+ )
103
+
104
+ ts_behind = False
105
+ p_ts = _parse_iso(p_updated)
106
+ b_ts = _parse_iso(b_updated)
107
+ if p_ts is not None and b_ts is not None:
108
+ try:
109
+ ts_behind = p_ts < b_ts
110
+ except TypeError:
111
+ # naive vs aware datetime — cannot order; defer to stage compare.
112
+ ts_behind = False
113
+
114
+ return {
115
+ "primary_behind": bool(stage_behind or ts_behind),
116
+ "primary_last_updated": p_updated,
117
+ "backup_last_updated": b_updated,
118
+ "primary_stage": p_stage,
119
+ "backup_stage": b_stage,
120
+ }
121
+
122
+
123
+ def run(state_file: str, backup_file: Optional[str]) -> int:
124
+ state = _load_state(Path(state_file))
125
+ if state is None:
126
+ return _err(f"State not readable as a mapping: {state_file}", "STATE_UNREADABLE")
127
+
128
+ campaign = state.get("campaign", {}) or {}
129
+ result: Dict[str, Any] = {
130
+ "campaign_name": campaign.get("name", ""),
131
+ "current_stage": campaign.get("current_stage"),
132
+ "last_updated": campaign.get("last_updated"),
133
+ **_counts(state),
134
+ "backup_comparison": None,
135
+ }
136
+
137
+ if backup_file:
138
+ backup = _load_state(Path(backup_file))
139
+ if backup is not None:
140
+ result["backup_comparison"] = _compare_backup(state, backup)
141
+
142
+ json.dump(result, sys.stdout, separators=(",", ":"), default=str)
143
+ sys.stdout.write("\n")
144
+ return 0
145
+
146
+
147
+ def main(argv: Optional[list] = None) -> int:
148
+ parser = argparse.ArgumentParser(
149
+ prog="campaign-status",
150
+ description="Summarize campaign state and (optionally) compare it to its backup.",
151
+ )
152
+ parser.add_argument("--state-file", required=True, help="Path to _campaign-state.yaml")
153
+ parser.add_argument(
154
+ "--backup-file",
155
+ help="Path to _campaign-state.yaml.bak; when given, emit backup_comparison",
156
+ )
157
+ args = parser.parse_args(argv)
158
+ return run(args.state_file, args.backup_file)
159
+
160
+
161
+ if __name__ == "__main__":
162
+ raise SystemExit(main())
@@ -1,4 +1,4 @@
1
- # Machine-generated campaign brief — resume context for fresh sessions (FR-35)
1
+ # Machine-generated campaign brief — resume context for fresh sessions
2
2
  # Populated by step-01-setup.md from operator inputs
3
3
 
4
4
  # Campaign identifier
@@ -28,7 +28,7 @@ These rules apply to every step in this workflow:
28
28
  - Never include content in SKILL.md that cannot be cited to source code
29
29
  - Only load one step file at a time — never preload future steps
30
30
  - Always communicate in `{communication_language}`
31
- - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
31
+ - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action, logging each auto-decision to the in-context `headless_decisions[]` buffer AND appending it as a JSON line to the on-disk auto-decision sink (established at step 1 §3) the moment it lands, so the audit trail survives context compaction before step 5 first writes the evidence report
32
32
 
33
33
  ## Stages
34
34
 
@@ -58,7 +58,7 @@ These rules apply to every step in this workflow:
58
58
  | Aspect | Detail |
59
59
  |--------|--------|
60
60
  | **Inputs** | brief_path (path to skill-brief.yaml) [required], --batch [optional] |
61
- | **Gates** | step 2: Choice Gate [P] (if match) | step 3: Review Gate [C] |
61
+ | **Gates** | step 2: Choice Gate [P] (if match) | step 3: Review Gate [C] | step 6: Content-Quality Gate [C] (if novel tessl suggestions) |
62
62
  | **Outputs** | SKILL.md, context-snippet.md, metadata.json, provenance-map.json, evidence-report.md, references/ |
63
63
  | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true |
64
64
 
@@ -69,4 +69,15 @@ These rules apply to every step in this workflow:
69
69
 
70
70
  2. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in preferences.yaml. Default: false.
71
71
 
72
- 3. Load, read the full file, and then execute `references/load-brief.md` to begin the workflow.
72
+ 3. **Resolve workflow customization.** Run:
73
+
74
+ ```bash
75
+ python3 {project-root}/_bmad/scripts/resolve_customization.py \
76
+ --skill {skill-root} --key workflow
77
+ ```
78
+
79
+ The script merges the three customization layers per `bmad-customize`'s structural merge rules (scalars override, arrays append): `{skill-root}/customize.toml` (bundled defaults), `_bmad/custom/skf-create-skill.toml` under `{project-root}` (team overrides, committed), and `_bmad/custom/skf-create-skill.user.toml` under `{project-root}` (personal overrides, gitignored). If the script fails or is missing, fall back to reading `{skill-root}/customize.toml` directly.
80
+
81
+ Apply the resolved values so the surface is not a silent no-op: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (entries prefixed `file:` are paths or globs whose contents load as facts); and stash `{onCompleteCommand}` ← `workflow.on_complete` (empty string = no-op) for the final stage to invoke after the result JSON and metadata.json are finalized. After activation completes, execute each entry in `workflow.activation_steps_append` in order.
82
+
83
+ 4. Load, read the full file, and then execute `references/load-brief.md` to begin the workflow.
@@ -1,6 +1,6 @@
1
1
  # SKILL.md Compilation Assembly Rules
2
2
 
3
- ## Frontmatter (REQUIRED — agentskills.io compliance)
3
+ ## Frontmatter (agentskills.io compliance)
4
4
 
5
5
  ```yaml
6
6
  ---
@@ -15,8 +15,8 @@ description: >
15
15
  **Frontmatter rules:**
16
16
 
17
17
  - `name`: lowercase alphanumeric + hyphens only, must match the skill output directory name. Prefer gerund form (`processing-pdfs`, `analyzing-spreadsheets`) for clarity.
18
- - `description`: non-empty, max 1024 chars, optimized for agent discovery. **MUST use third-person voice** ("Processes Excel files..." not "I can help you..." or "You can use this to...") AND **MUST include a trigger phrase** so agents know when to match — one of `Use when …`, `Triggers on …`, or `Reach for this when …`. When using `Use when `, follow with a **gerund** (`Use when building/processing/analyzing X…`) or a noun-phrase clause (`Use when the user requests to "X"`); never a bare indicative verb like `builds`/`processes`, because `skill-check --fix` will prepend a literal `Use when ` to a description missing the trigger phrase, producing ungrammatical output (`Use when builds X…`). Inconsistent point-of-view or a missing trigger phrase causes discovery problems since the description is injected into the system prompt. See `skf-brief-skill/assets/description-voice-examples.md` for the canonical voice palette.
19
- - **`description` must NOT contain angle brackets** — neither standalone placeholders like `<name>`, `<component>`, `<path>`, nor inline generics like `` `Array<T>` `` or `` `Meta<typeof X>` ``. Both `skill-check`'s `description_field` validator and `tessl`'s deterministic description check parse the frontmatter description as a raw string and reject any `<` or `>`, regardless of markdown context (backticks do NOT protect content here). A rejected description fails the review with 0% score. When the natural phrasing would use angle brackets:
18
+ - `description`: non-empty, max 1024 chars, optimized for agent discovery. Use third-person voice ("Processes Excel files..." not "I can help you..." or "You can use this to...") and include a trigger phrase so agents know when to match — one of `Use when …`, `Triggers on …`, or `Reach for this when …`. When using `Use when `, follow with a **gerund** (`Use when building/processing/analyzing X…`) or a noun-phrase clause (`Use when the user requests to "X"`); never a bare indicative verb like `builds`/`processes`, because `skill-check --fix` will prepend a literal `Use when ` to a description missing the trigger phrase, producing ungrammatical output (`Use when builds X…`). Inconsistent point-of-view or a missing trigger phrase causes discovery problems since the description is injected into the system prompt. See `skf-brief-skill/assets/description-voice-examples.md` for the canonical voice palette.
19
+ - **The `description` cannot contain angle brackets** — neither standalone placeholders like `<name>`, `<component>`, `<path>`, nor inline generics like `` `Array<T>` `` or `` `Meta<typeof X>` ``. Both `skill-check`'s `description_field` validator and `tessl`'s deterministic description check parse the frontmatter description as a raw string and reject any `<` or `>`, regardless of markdown context (backticks do not protect content here). A rejected description fails the review with 0% score. When the natural phrasing would use angle brackets:
20
20
  - Prefer the curly-brace form in prose: `{name}`, `{component-id}`, `{path}` — readable and tessl-safe.
21
21
  - Uppercase placeholders are also acceptable: `NAME`, `COMPONENT_ID`.
22
22
  - For code-ish fragments, use curly braces in place of angle brackets inside the backticks: `` `Meta{typeof X}` ``, `` `Array{T}` ``.
@@ -27,7 +27,7 @@ description: >
27
27
 
28
28
  ## Two-Tier Assembly
29
29
 
30
- **CRITICAL: Two-tier assembly.** SKILL.md must retain actionable inline content that survives `split-body` extraction. Assemble Tier 1 sections first (always inline), then Tier 2 sections (reference-eligible, may be extracted by split-body).
30
+ **Two-tier assembly.** SKILL.md must retain actionable inline content that survives `split-body` extraction. Assemble Tier 1 sections first (always inline), then Tier 2 sections (reference-eligible, may be extracted by split-body).
31
31
 
32
32
  ### Tier 1 — Always Inline (must survive split-body)
33
33
 
@@ -104,7 +104,7 @@ These sections form the essential standalone body. Target: **under 300 lines tot
104
104
 
105
105
  Assemble Sections 9-11 (Full API Reference, Full Type Definitions, Full Integration Patterns) as defined in the skill-sections data file. These contain full detail and are split into `references/` when the body exceeds 500 lines. Include T2 annotations from enrichment in the Full API Reference (Deep tier only).
106
106
 
107
- **CRITICAL — Tier 2 differentiation from Tier 1:** Tier 2 Full API Reference must contain content that is NOT present in Tier 1's Key API Summary. Specifically:
107
+ **Tier 2 differentiation from Tier 1:** Tier 2 Full API Reference must contain content that is not present in Tier 1's Key API Summary. Specifically:
108
108
 
109
109
  - **Full parameter tables** with types, defaults, and required/optional flags (Tier 1 only lists key params)
110
110
  - **Return value details** including structure, types, and error conditions
@@ -112,7 +112,7 @@ Assemble Sections 9-11 (Full API Reference, Full Type Definitions, Full Integrat
112
112
  - **Usage examples** from source tests or documentation (Tier 1 has signature-only references)
113
113
  - **Edge cases and constraints** — parameter validation rules, size limits, behavioral notes
114
114
 
115
- Do NOT repeat Tier 1's name/purpose/key-params table format in Tier 2. Tier 2 is a deep reference, not a reformatted summary. This distinction prevents conciseness scorers from flagging the two-tier design as redundancy.
115
+ Do not repeat Tier 1's name/purpose/key-params table format in Tier 2. Tier 2 is a deep reference, not a reformatted summary. This distinction prevents conciseness scorers from flagging the two-tier design as redundancy.
116
116
 
117
117
  ### Component Library Assembly Overrides
118
118
 
@@ -267,8 +267,8 @@ Replace per-function subsections with `references/pattern-*.md` groupings: one r
267
267
 
268
268
  - `stats.exports_documented` MAY be set to the Pattern Surface row count as a proxy, but its semantics change: it counts authored pattern surfaces, not public exports. Emit an adjacent `stats.pattern_surfaces_documented` with the same integer so downstream consumers (test-skill, feasibility, discovery) can discriminate.
269
269
  - `exports_public_api` / `exports_internal` / `exports_total` are not meaningful for a reference app — omit them, or set all three to the Pattern Surface count with a note in `stats.notes`: `"reference-app: export counts are pattern-surface proxies, not library exports"`.
270
- - Do NOT fabricate signature / type-coverage data from pattern surfaces. skf-test-skill will skip those categories when metadata flags a reference-app (same skip path as `stackSkill` once that flag is wired — see scoring-rules.md).
271
- - Do NOT emit `effective_denominator` for reference-app scope, even when the source is a monorepo. `effective_denominator` counts public exports from the authoring-surface barrel — a library concept that `pattern_surfaces_documented` already replaces here. A reference-app-in-monorepo literally satisfies `compile.md` §4's emit-conditions (monorepo + non-`full-library` + stratified `scope.notes`), so this carve-out takes precedence: pattern-surface coverage is the reference-app basis, and skf-test-skill skips library-export coverage for it.
270
+ - Do not fabricate signature / type-coverage data from pattern surfaces. skf-test-skill will skip those categories when metadata flags a reference-app (same skip path as `stackSkill` once that flag is wired — see scoring-rules.md).
271
+ - Do not emit `effective_denominator` for reference-app scope, even when the source is a monorepo. `effective_denominator` counts public exports from the authoring-surface barrel — a library concept that `pattern_surfaces_documented` already replaces here. A reference-app-in-monorepo literally satisfies `compile.md` §4's emit-conditions (monorepo + non-`full-library` + stratified `scope.notes`), so this carve-out takes precedence: pattern-surface coverage is the reference-app basis, and skf-test-skill skips library-export coverage for it.
272
272
 
273
273
  **Language / spec-reference sub-shape:** A reference app that documents an engine- or spec-versioned **language** — a query language, grammar, or DSL (e.g. SurrealQL @ SurrealDB) whose value is construct idioms rather than wiring or exports — is a recognized reference-app sub-shape. Keep `scope.type: "reference-app"` (there is no separate enum value), so every carve-out above applies unchanged (`pattern_surfaces_documented` proxy, no `effective_denominator`, test-skill signature/type skip). Adapt the three overrides to the language's surface instead of app wiring:
274
274
 
@@ -288,27 +288,27 @@ Replace per-function subsections with `references/pattern-*.md` groupings: one r
288
288
  - **Tier 2** groups `references/*.md` by **language concern** (e.g. `statements.md`, `functions.md`, `types.md`) — one file per construct family — instead of `pattern-*.md` wiring concerns.
289
289
  - **metadata.json:** `pattern_surfaces_documented` counts the documented construct areas; the no-`effective_denominator` carve-out applies (a language has no export barrel). The brief's `language` field records the **documented** language (e.g. `surrealql`), which may differ from the source language it was extracted from (e.g. `rust`) — see `skf-analyze-source/assets/skill-brief-schema.md`.
290
290
 
291
- **When this clause does NOT apply:** `full-library`, `specific-modules`, `public-api`, `component-library`, or `docs-only`. Those scope types have their own assembly semantics and export-count conventions — do not mix.
291
+ **When this clause does not apply:** `full-library`, `specific-modules`, `public-api`, `component-library`, or `docs-only`. Those scope types have their own assembly semantics and export-count conventions — do not mix.
292
292
 
293
293
  ### Whole-Language Reference Assembly Overrides
294
294
 
295
295
  A **whole-language reference** is a compiler/interpreter repo (rustc, CPython, the Go toolchain, TypeScript) enriched with the language's canonical prose — the guide/Book and the standard/library docs. It maps to `scope.type: full-library`, but its value to a skill consumer is the **language**, not the compiler's internal exports. The standard library-export layout above would foreground compiler-internal signatures and bury the prose; these overrides invert that.
296
296
 
297
- **GATE — apply ONLY when this is a whole-language reference.** The brief carries ≥1 `doc_urls` entry with `source: language-registry` (equivalently, `skf-derive-assembly-shape.py` returns `assembly_shape: "whole-language-reference"`). This is a structured, schema-validated signal — NOT a `scope.notes` substring — so an ordinary `full-library` library, a parser *library* (pest/lalrpop — its code IS the product, so §6b seeds no corpora), a component-library, a reference-app (including the language/spec-reference DSL sub-shape, which stays `scope.type: reference-app` and is handled by the reference-app override above), and a docs-only brief all fail the gate. When the gate does NOT fire, **skip this entire section** — the standard Tier 1 (sections 1–8) and Tier 2 (9–11) layout and the signature-fidelity rule run unchanged, so non-whole-language skills assemble byte-identically.
297
+ **GATE — apply ONLY when this is a whole-language reference.** The brief carries ≥1 `doc_urls` entry with `source: language-registry` (equivalently, `skf-derive-assembly-shape.py` returns `assembly_shape: "whole-language-reference"`). This is a structured, schema-validated signal — not a `scope.notes` substring — so an ordinary `full-library` library, a parser *library* (pest/lalrpop — its code IS the product, so §6b seeds no corpora), a component-library, a reference-app (including the language/spec-reference DSL sub-shape, which stays `scope.type: reference-app` and is handled by the reference-app override above), and a docs-only brief all fail the gate. When the gate does not fire, **skip this entire section** — the standard Tier 1 (sections 1–8) and Tier 2 (9–11) layout and the signature-fidelity rule run unchanged, so non-whole-language skills assemble byte-identically.
298
298
 
299
299
  When the gate fires, the assembler has a `language_guide[]` artifact from step 3c §4a (the retained corpora prose, carved out of the T3-vs-T1 conflict rule). Apply these overrides:
300
300
 
301
- **Empty-guide guard (check first):** If `language_guide[]` is absent or every entry's `prose` is null (all registry corpora failed to fetch), do NOT emit a thin Language Guide above a full internals section — fall back to the standard layout and record a warning in `evidence-report.md`: "Whole-language reference gated but no Language-Guide prose was retained — assembled as a standard library skill." This prevents the inverse-value outcome (prose section empty, compiler internals dominant).
301
+ **Empty-guide guard (check first):** If `language_guide[]` is absent or every entry's `prose` is null (all registry corpora failed to fetch), do not emit a thin Language Guide above a full internals section — fall back to the standard layout and record a warning in `evidence-report.md`: "Whole-language reference gated but no Language-Guide prose was retained — assembled as a standard library skill." This prevents the inverse-value outcome (prose section empty, compiler internals dominant).
302
302
 
303
303
  **New Section 1b — Language Guide (Tier 1, foregrounded):** Immediately after Section 1 (Overview) and BEFORE Quick Start, emit the skill's primary content from `language_guide[]`. One subsection per corpus (`{label}`), carrying the retained prose — language concepts, idioms, and usage examples — each block cited `[EXT:{url}]`. This is the section an agent reads to learn the language; it survives split-body as Tier 1.
304
304
 
305
- **Section 2 (Quick Start) — language-usage override:** Build the runnable examples from the Language-Guide prose (writing and running code *in* the language — a minimal program, a common idiom), cited `[EXT:{url}]`, NOT from compiler-internal export call chains. Fall back to the standard signature-only Quick Start only if the guide yields no examples.
305
+ **Section 2 (Quick Start) — language-usage override:** Build the runnable examples from the Language-Guide prose (writing and running code *in* the language — a minimal program, a common idiom), cited `[EXT:{url}]`, not from compiler-internal export call chains. Fall back to the standard signature-only Quick Start only if the guide yields no examples.
306
306
 
307
307
  **Section 3 (Common Workflows) — "Writing {language}" override:** Replace function-call-chain workflows over compiler exports with common language tasks (define a type, handle errors, organize a module), each a short idiom from the guide.
308
308
 
309
309
  **Section 4 (Key API Summary) — demote compiler internals:** Replace the top-of-body compiler-export function table with a one-paragraph "Standard Library & Language Surface" pointer into the Language Guide. Move the AST/compiler-internal exports into a late-ordered Tier 2 subsection `### Compiler Internals (reference only)` with a one-line note: "Internal implementation surface of the {language} toolchain — most consumers want the Language Guide above, not these."
310
310
 
311
- **Signature fidelity is preserved (NOT relaxed):** this is an ordering/prominence change only. Any compiler signatures that DO appear (in Compiler Internals) keep their T1/AST-authoritative params and return types per Section 1b of `compile.md` and rule 7 below. The override never substitutes prose-derived signatures for AST-extracted ones; it changes which content leads, not which tier wins a per-export signature conflict.
311
+ **Signature fidelity is preserved (not relaxed):** this is an ordering/prominence change only. Any compiler signatures that DO appear (in Compiler Internals) keep their T1/AST-authoritative params and return types per Section 1b of `compile.md` and rule 7 below. The override never substitutes prose-derived signatures for AST-extracted ones; it changes which content leads, not which tier wins a per-export signature conflict.
312
312
 
313
313
  **metadata.json:** the skill stays `scope.type: full-library`; export-count stats are still emitted. (A whole-language skill's low public-API coverage versus the full compiler surface is expected — coverage-gate semantics for this shape are tracked separately and out of scope here.)
314
314
 
@@ -316,7 +316,7 @@ When the gate fires, the assembler has a `language_guide[]` artifact from step 3
316
316
 
317
317
  1. Assemble all Tier 1 sections first — these form the essential standalone body
318
318
  2. Assemble all Tier 2 sections after — these are progressive disclosure detail
319
- 3. Tier 1 content MUST be under 300 lines (excluding frontmatter)
319
+ 3. Tier 1 content stays under 300 lines (excluding frontmatter)
320
320
  4. If Tier 1 alone exceeds 300 lines, reduce Key API Summary and Architecture at a Glance
321
321
  5. Tier 1 sections are kept short enough that `split-body` targets the larger Tier 2 sections (`## Full ...` headings) instead
322
322
  6. After split-body, SKILL.md must still contain all Tier 1 sections with actionable content
@@ -330,32 +330,11 @@ When the gate fires, the assembler has a `language_guide[]` artifact from step 3
330
330
 
331
331
  ### Content Quality Rules
332
332
 
333
- These rules apply to all content assembled in SKILL.md and reference files.
333
+ These SKF-specific rules apply to all content assembled in SKILL.md and reference files. Generic authoring craft — matching instruction freedom to task fragility, consistent terminology, avoiding time-sensitive instructions, progress checklists for multi-step workflows, and the plan-validate-execute pattern for batch/destructive operations — is assumed and not re-taught here. Only the two rules below encode a non-obvious SKF constraint or downstream-validator quirk that an author would not otherwise infer.
334
334
 
335
- **Degrees of freedom:** Match instruction specificity to the task's fragility and variability:
336
- - **High freedom** (text guidance): When multiple approaches are valid and context determines the best one. Example: code review patterns, architecture suggestions.
337
- - **Medium freedom** (pseudocode/parameterized scripts): When a preferred pattern exists but variation is acceptable. Example: configuration templates, report generation.
338
- - **Low freedom** (exact scripts, no parameters): When operations are fragile and consistency is critical. Example: database migrations, deployment sequences. Use "Run exactly this" language.
335
+ **Zero-hallucination examples:** Include input/output or usage examples ONLY when concrete pairs exist in source tests or official docs — 2-3 such pairs convey the desired style and detail more clearly than a description alone. If no examples exist in source, note the gap rather than fabricating pairs — zero hallucination applies.
339
336
 
340
- **Consistent terminology:** Choose one term per concept and use it throughout the skill. Do not mix synonyms (e.g., "API endpoint" vs "URL" vs "route", or "field" vs "box" vs "element"). Consistency helps agents understand and follow instructions deterministically.
341
-
342
- **Avoid time-sensitive information:** Do not include date-conditional instructions ("If before August 2025, use the old API"). Instead, document the current method and place deprecated patterns in a collapsible "Old patterns" section with the deprecation date.
343
-
344
- **Template and examples patterns:**
345
- - **For strict requirements** (API responses, data formats): Provide an exact template with "ALWAYS use this exact structure" language.
346
- - **For flexible guidance** (reports, analysis): Provide a sensible default template with "Adjust sections as needed" language.
347
- - **Input/output examples:** When output quality depends on seeing examples and concrete pairs exist in source tests or official docs, include 2-3 input/output pairs sourced from those tests or docs. Examples help agents understand desired style and detail more clearly than descriptions alone. If no examples exist in source, note the gap rather than fabricating pairs — zero hallucination applies.
348
-
349
- **Generic-plus-signature code spans:** When documenting a generic class constructor or factory signature, do NOT place the generic brackets and the parameter list inside a single inline code span. `skill-check`'s `links.local_markdown_resolves` validator parses `` `ClassName[T](key: str)` `` as a broken markdown link (`[T]` becomes the link text, `(key: str)` becomes the URL) and emits a `broken local link` warning on the Links axis, regardless of the surrounding backticks. This applies to Tier 1 Key Types, Tier 2 Full API Reference, and reference files. Safe alternatives:
337
+ **Generic-plus-signature code spans:** When documenting a generic class constructor or factory signature, do not place the generic brackets and the parameter list inside a single inline code span. `skill-check`'s `links.local_markdown_resolves` validator parses `` `ClassName[T](key: str)` `` as a broken markdown link (`[T]` becomes the link text, `(key: str)` becomes the URL) and emits a `broken local link` warning on the Links axis, regardless of the surrounding backticks. This applies to Tier 1 Key Types, Tier 2 Full API Reference, and reference files. Safe alternatives:
350
338
  - Split into two code spans: `` `ClassName[T]` — dataclass with fields `(key: str, value: int)` ``
351
339
  - Drop the explicit constructor and describe fields in prose: `` `ClassName[T]` — generic container parameterized by `T`, with field `key: str` ``
352
340
  - Use the curly-brace substitution used for frontmatter: `` `ClassName{T}(key: str)` `` (readable, avoids both markdown-link and angle-bracket parsing)
353
-
354
- **Workflow checklist pattern:** When a skill includes multi-step workflows, provide a copy-paste checklist that agents can track progress against:
355
- ```markdown
356
- Copy this checklist and track your progress:
357
- - [ ] Step 1: {action}
358
- - [ ] Step 2: {action}
359
- ```
360
-
361
- **Verifiable intermediate outputs:** For skills involving batch operations, destructive changes, or complex validation, recommend the plan-validate-execute pattern: create a structured plan file (e.g., `changes.json`), validate it with a script, then execute. This catches errors before changes are applied.