bmad-module-skill-forge 0.7.1 → 0.7.3

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/knowledge/qmd-registry.md +6 -1
  3. package/src/workflows/analyze-source/data/skill-brief-schema.md +3 -1
  4. package/src/workflows/analyze-source/steps-c/step-05-recommend.md +4 -4
  5. package/src/workflows/brief-skill/data/skill-brief-schema.md +18 -1
  6. package/src/workflows/brief-skill/steps-c/step-01-gather-intent.md +16 -0
  7. package/src/workflows/brief-skill/steps-c/step-02-analyze-target.md +42 -6
  8. package/src/workflows/brief-skill/steps-c/step-03-scope-definition.md +19 -5
  9. package/src/workflows/brief-skill/steps-c/step-04-confirm-brief.md +24 -4
  10. package/src/workflows/brief-skill/steps-c/step-05-write-brief.md +34 -9
  11. package/src/workflows/brief-skill/workflow.md +1 -1
  12. package/src/workflows/create-skill/data/compile-assembly-rules.md +1 -0
  13. package/src/workflows/create-skill/data/extraction-patterns.md +54 -4
  14. package/src/workflows/create-skill/data/skill-sections.md +2 -1
  15. package/src/workflows/create-skill/data/source-resolution-protocols.md +15 -1
  16. package/src/workflows/create-skill/steps-c/step-01-load-brief.md +4 -4
  17. package/src/workflows/create-skill/steps-c/step-02b-ccc-discover.md +4 -2
  18. package/src/workflows/create-skill/steps-c/step-03-extract.md +1 -1
  19. package/src/workflows/create-skill/steps-c/step-03b-fetch-temporal.md +18 -3
  20. package/src/workflows/create-skill/steps-c/step-03c-fetch-docs.md +11 -5
  21. package/src/workflows/create-skill/steps-c/step-05-compile.md +19 -3
  22. package/src/workflows/create-skill/steps-c/step-06-validate.md +10 -6
  23. package/src/workflows/create-skill/steps-c/step-07-generate-artifacts.md +1 -1
  24. package/src/workflows/quick-skill/steps-c/step-04-compile.md +1 -1
  25. package/src/workflows/test-skill/data/output-section-formats.md +15 -0
  26. package/src/workflows/test-skill/data/scoring-rules.md +12 -0
  27. package/src/workflows/test-skill/data/source-access-protocol.md +8 -2
  28. package/src/workflows/test-skill/steps-c/step-03-coverage-check.md +41 -5
  29. package/src/workflows/test-skill/steps-c/step-04-coherence-check.md +48 -4
  30. package/src/workflows/test-skill/steps-c/step-04b-external-validators.md +9 -1
  31. package/src/workflows/test-skill/steps-c/step-05-score.md +2 -0
  32. package/tools/cli/lib/installer.js +4 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-module-skill-forge",
4
- "version": "0.7.1",
4
+ "version": "0.7.3",
5
5
  "description": "BMAD module — Turn code and docs into instructions AI agents can actually follow. Progressive capability tiers (Quick/Forge/Forge+/Deep).",
6
6
  "keywords": [
7
7
  "bmad",
@@ -40,8 +40,13 @@ qmd_collections:
40
40
  source_workflow: "brief-skill"
41
41
  skill_name: "my-lib"
42
42
  created_at: "2026-03-14"
43
+ # status: "pending" # Optional — see below
43
44
  ```
44
45
 
46
+ **Optional field: `status`**
47
+
48
+ The `status` field is only present when QMD embed verification fails during collection creation. When `status: "pending"` is set, the collection exists in QMD but vector embeddings may be incomplete — only BM25 keyword `search` is reliable. `vector_search` and `deep_search` may return no results until re-embedded. Collections without a `status` field are fully operational. The setup-forge janitor should flag `"pending"` collections for re-embedding.
49
+
45
50
  ### Collection Types
46
51
 
47
52
  | Type | Producer | Content | Primary Consumers |
@@ -122,7 +127,7 @@ These registries are orthogonal — they never reference each other, and their j
122
127
 
123
128
  ```
124
129
  qmd collection remove {name}-extraction (if exists)
125
- qmd collection add skills/{name} --name {name}-extraction --mask "**/*"
130
+ qmd collection add {project-root}/skills/{name} --name {name}-extraction --mask "**/*"
126
131
  qmd embed
127
132
  ```
128
133
 
@@ -9,7 +9,7 @@ Defines the output contract for skill-brief.yaml files generated by analyze-sour
9
9
  | Field | Type | Constraint | Description |
10
10
  |-------|------|------------|-------------|
11
11
  | name | string | kebab-case `[a-z0-9-]+` | Unique skill identifier |
12
- | version | string | Semantic `X.Y.Z` | Auto-detect from source (see Version Detection below), fall back to `1.0.0` |
12
+ | version | string | Semantic version (`X.Y.Z` or `X.Y.Z-prerelease`) | Auto-detect from source (see Version Detection below), fall back to `1.0.0` |
13
13
  | source_repo | string | GitHub URL or local path | Repository or project root (optional when `source_type: "docs-only"`) |
14
14
  | language | string | Recognized language | Primary programming language |
15
15
  | scope | object | See Scope Object below | Boundary definition |
@@ -49,6 +49,8 @@ If detection succeeds, use the detected version. If it fails or returns a non-se
49
49
 
50
50
  The create-skill workflow (step-03-extract) also performs version reconciliation at extraction time — if the source version has changed since the brief was created, the extraction step warns and uses the source version.
51
51
 
52
+ **Pre-release handling:** If the detected version contains a pre-release tag (e.g., `1.0.0-beta.0`, `2.0.0-rc.1`), preserve it as-is. Pre-release tags are valid semver and must not be stripped. When comparing versions during reconciliation, use semver-aware comparison that respects pre-release ordering.
53
+
52
54
  ## Scope Object
53
55
 
54
56
  ```yaml
@@ -5,8 +5,8 @@ description: 'Present unit recommendations with rationale for user confirmation
5
5
  nextStepFile: './step-06-generate-briefs.md'
6
6
  outputFile: '{forge_data_folder}/analyze-source-report-{project_name}.md'
7
7
  schemaFile: '../data/skill-brief-schema.md'
8
- advancedElicitationTask: '{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.xml'
9
- partyModeWorkflow: '{project-root}/_bmad/core/workflows/party-mode/workflow.md'
8
+ advancedElicitationSkill: '/bmad-advanced-elicitation'
9
+ partyModeSkill: '/bmad-party-mode'
10
10
  ---
11
11
 
12
12
  # Step 5: Recommend
@@ -193,8 +193,8 @@ Display: "**Select an Option:** [A] Advanced Elicitation [P] Party Mode [D] Disc
193
193
 
194
194
  #### Menu Handling Logic:
195
195
 
196
- - IF A: Execute {advancedElicitationTask}, and when finished redisplay the menu
197
- - IF P: Execute {partyModeWorkflow}, and when finished redisplay the menu
196
+ - IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
197
+ - IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
198
198
  - IF D: Accept a new repo path/URL from the user. Run a lightweight scan + classify (subset of steps 02-03) for the new source only. Merge new units into the existing report and update `project_paths[]` in frontmatter. Run export mapping for the new units (same logic as step 04 section 2). Generate recommendation cards for the new units and present them for confirmation. Then redisplay this menu.
199
199
  - IF C: Save recommendations to {outputFile}, update frontmatter, then load, read entire file, then execute {nextStepFile}
200
200
  - IF Any other comments or queries: help user respond then [Redisplay Menu Options](#8-present-menu-options)
@@ -5,7 +5,7 @@
5
5
  | Field | Type | Constraint | Description |
6
6
  |-------|------|------------|-------------|
7
7
  | name | string | kebab-case `[a-z0-9-]+` | Unique skill identifier |
8
- | version | string | Semantic `X.Y.Z` | Auto-detect from source (see Version Detection below), fall back to `1.0.0` |
8
+ | version | string | Semantic version (`X.Y.Z` or `X.Y.Z-prerelease`) | Auto-detect from source (see Version Detection below), fall back to `1.0.0` |
9
9
  | source_repo | string | GitHub URL or local path | Repository or project root (optional when `source_type: "docs-only"`) |
10
10
  | language | string | Recognized language | Primary programming language |
11
11
  | scope | object | See Scope Object below | Boundary definition |
@@ -45,6 +45,8 @@ If detection succeeds, use the detected version. If it fails or returns a non-se
45
45
 
46
46
  The create-skill workflow (step-03-extract) also performs version reconciliation at extraction time — if the source version has changed since the brief was created, the extraction step warns and uses the source version.
47
47
 
48
+ **Pre-release handling:** If the detected version contains a pre-release tag (e.g., `1.0.0-beta.0`, `2.0.0-rc.1`), preserve it as-is. Pre-release tags are valid semver and must not be stripped. When comparing versions during reconciliation, use semver-aware comparison that respects pre-release ordering.
49
+
48
50
  ## Scope Object Structure
49
51
 
50
52
  ```yaml
@@ -106,6 +108,21 @@ Scope: {scope.type}
106
108
  Exclude: {scope.exclude patterns, one per line}
107
109
  Notes: {scope.notes}
108
110
 
111
+ {If source_type is "docs-only":}
112
+ Source Type: docs-only
113
+ Doc URLs:
114
+ {doc_urls, one per line with labels}
115
+
116
+ {If source_type is "source" AND supplemental doc_urls collected:}
117
+ Supplemental Docs:
118
+ {doc_urls, one per line with labels}
119
+
120
+ {If scripts_intent or assets_intent was explicitly set (not default "detect"):}
121
+ Scripts: {scripts_intent}
122
+ Assets: {assets_intent}
123
+
124
+ Source Authority: {source_authority}
125
+
109
126
  Version: {version}
110
127
  Created: {created}
111
128
  Created by: {created_by}
@@ -82,6 +82,9 @@ We'll work through this together:
82
82
  3. **Then:** Define scope boundaries
83
83
  4. **Finally:** Confirm and write the brief
84
84
 
85
+ {If tier override was applied:}
86
+ **Your forge tier:** {override tier} (overridden from {original tier}) — this determines what compilation capabilities are available.
87
+ {Else:}
85
88
  **Your forge tier:** {detected tier} — this determines what compilation capabilities are available.
86
89
 
87
90
  Let's get started."
@@ -110,6 +113,14 @@ Wait for user response.
110
113
  - Optionally ask: "Are there any documentation URLs you'd like to include for supplemental context? (These will be fetched as T3 external references.)"
111
114
  - If yes: collect doc URLs into `doc_urls`
112
115
 
116
+ **Source authority (for all source-type skills):**
117
+ "**Are you the maintainer of this library, or creating a community skill?**"
118
+ - If maintainer: set `source_authority: "official"`
119
+ - If community user: set `source_authority: "community"` (default)
120
+ - If internal/proprietary: set `source_authority: "internal"`
121
+
122
+ Default to `"community"` if user does not specify or skips. For `docs-only` skills, `source_authority` is always forced to `"community"`.
123
+
113
124
  Confirm the target.
114
125
 
115
126
  ### 4. Gather User Intent
@@ -154,6 +165,10 @@ Wait for confirmation or alternative.
154
165
  - **Intent:** {user's intent summary}
155
166
  - **Scope hints:** {any hints, or "None — we'll define scope after analysis"}
156
167
  - **Skill name:** {confirmed name}
168
+ - **Source type:** {source or docs-only}
169
+ - **Source authority:** {official/community/internal}
170
+ {If doc_urls collected:}
171
+ - **Doc URLs:** {count} supplemental documentation URLs
157
172
  - **Forge tier:** {tier}
158
173
 
159
174
  Ready to analyze the target repository?"
@@ -187,6 +202,7 @@ ONLY WHEN C is selected and target repository is confirmed will you load and rea
187
202
  - User intent captured with enough context for scoping
188
203
  - Skill name derived and confirmed
189
204
  - Scope hints noted (if provided)
205
+ - Source type and source authority captured
190
206
  - User ready to proceed to analysis
191
207
 
192
208
  ### ❌ SYSTEM FAILURE:
@@ -60,6 +60,12 @@ To analyze the target repository by resolving its location, reading its structur
60
60
  **For GitHub URLs:**
61
61
  - Use `gh api repos/{owner}/{repo}` to verify the repository exists
62
62
  - Use `gh api repos/{owner}/{repo}/git/trees/HEAD?recursive=1` to get the file tree
63
+
64
+ **Truncation detection:** After receiving the tree response, check the `truncated` field in the JSON output. If `truncated: true`:
65
+ - Display: "Note: GitHub API returned a truncated tree response ({count} items). Full analysis may require a local clone."
66
+ - Record in analysis summary: "Tree listing is partial — some files may not appear in the analysis."
67
+ - For very large repos (>1000 files in tree response): suggest local clone for complete analysis.
68
+
63
69
  - If inaccessible: **HALT** — "**Error:** Cannot access repository at {url}. Please verify the URL is correct and you have access. If private, ensure `gh auth` is configured."
64
70
 
65
71
  **For local paths:**
@@ -133,9 +139,16 @@ Based on detected language, identify public API surface:
133
139
 
134
140
  **Semantic Signals (Forge+ and Deep with ccc only):**
135
141
 
142
+ **Remote source guard:** If the target source was resolved via GitHub API (remote URL, not a local file path), skip this CCC subsection — CCC requires a local source index and cannot operate on remote-only sources. Note: "CCC semantic discovery skipped — target is remote. CCC will be available during create-skill if the source is cloned locally."
143
+
136
144
  If `tools.ccc` is true in forge-tier.yaml, supplement the module listing with a semantic discovery pass:
137
145
 
138
- Run `ccc_bridge.search("{repo_name} public API exports modules", source_path, top_k=10)` — **Tool resolution:** `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `ccc search` CLI. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
146
+ **CCC Semantic Discovery:**
147
+ - **Claude Code:** Use `/ccc search "{repo_name} public API exports modules" {source_path}`
148
+ - **Cursor:** Use `ccc` MCP server `search` tool with query `"{repo_name} public API exports modules"` and path `{source_path}`
149
+ - **CLI fallback:** `ccc search "{repo_name} public API exports modules" --path {source_path} --limit 10`
150
+
151
+ See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md) for full bridge-to-tool mapping.
139
152
 
140
153
  If results are returned, display:
141
154
 
@@ -146,6 +159,22 @@ This supplements — never replaces — the explicit module list above. CCC may
146
159
 
147
160
  If CCC is unavailable or returns no results: skip this subsection silently.
148
161
 
162
+ ### 4b. Detect Source Version
163
+
164
+ Attempt to auto-detect the source version using the rules from the skill-brief-schema.md Version Detection section:
165
+
166
+ **For Python:** Check `pyproject.toml` `[project] version` (static) → if `dynamic = ["version"]`, check `__init__.py` for `__version__` → `_version.py` if exists → `setup.py` `version=` → `git describe --tags --abbrev=0`
167
+ **For JavaScript/TypeScript:** Check `package.json` `"version"` field
168
+ **For Rust:** Check `Cargo.toml` `[package] version` (static) → if `version = { workspace = true }`, resolve from workspace root `Cargo.toml` → `git describe --tags --abbrev=0`
169
+ **For Go:** Check `go.mod` or `git describe --tags --abbrev=0`
170
+
171
+ **For GitHub repos:** Use `gh api repos/{owner}/{repo}/contents/{file}` to read version files (decode base64 content).
172
+ **For local repos:** Read the file directly.
173
+
174
+ Display: "**Detected version:** {version or 'Not detected — will default to 1.0.0'}"
175
+
176
+ If detection fails or returns a non-semver value: note that version will default to `"1.0.0"` and the user can override in step 04.
177
+
149
178
  ### 5. Report Analysis Summary
150
179
 
151
180
  Present the complete analysis:
@@ -169,6 +198,7 @@ Present the complete analysis:
169
198
  - Tests: {found/not found — location}
170
199
  - Docs: {found/not found — location}
171
200
  - Config: {list of config files found}
201
+ - Version: {detected version or "Not detected — defaulting to 1.0.0"}
172
202
 
173
203
  ---
174
204
 
@@ -179,16 +209,21 @@ Moving to scope definition where you'll choose what to include and exclude."
179
209
 
180
210
  ### 6. Auto-Proceed to Scope Definition
181
211
 
182
- Display: "**Proceeding to scope definition...**"
212
+ Display: "**Proceeding to scope definition...**
213
+
214
+ Review the analysis above. If anything looks wrong, let me know now — otherwise I'll proceed to scope definition."
215
+
216
+ Pause briefly for user input. If the user provides corrections or asks questions, address them and re-present any updated analysis findings. Then proceed.
183
217
 
184
218
  #### Menu Handling Logic:
185
219
 
186
- - After analysis report is presented to user, immediately load, read entire file, then execute {nextStepFile}
220
+ - After analysis report is presented to user and any corrections addressed, load, read entire file, then execute {nextStepFile}
187
221
 
188
222
  #### EXECUTION RULES:
189
223
 
190
- - This is an auto-proceed step — analysis is factual reporting with no user choices
191
- - Proceed directly to step 03 after presenting the analysis summary
224
+ - This is a soft auto-proceed step — present the pause prompt, wait briefly for user input
225
+ - If user provides corrections: address them, then proceed
226
+ - If no user input after a brief pause: proceed directly to step 03
192
227
 
193
228
  ## CRITICAL STEP COMPLETION NOTE
194
229
 
@@ -204,8 +239,9 @@ ONLY WHEN the analysis is complete and the summary has been presented to the use
204
239
  - Repository structure listed clearly
205
240
  - Primary language detected with confidence level
206
241
  - Top-level modules and exports identified
242
+ - Source version detected (or default noted)
207
243
  - Analysis summary presented factually
208
- - Auto-proceeded to scope definition
244
+ - Auto-proceeded to scope definition (with pause for corrections)
209
245
 
210
246
  ### ❌ SYSTEM FAILURE:
211
247
 
@@ -4,8 +4,8 @@ description: 'Collaboratively define skill scope boundaries using analysis findi
4
4
 
5
5
  nextStepFile: './step-04-confirm-brief.md'
6
6
  scopeTemplatesFile: '../data/scope-templates.md'
7
- advancedElicitationTask: '{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.xml'
8
- partyModeWorkflow: '{project-root}/_bmad/core/workflows/party-mode/workflow.md'
7
+ advancedElicitationSkill: '/bmad-advanced-elicitation'
8
+ partyModeSkill: '/bmad-party-mode'
9
9
  ---
10
10
 
11
11
  # Step 3: Scope Definition
@@ -90,7 +90,21 @@ Wait for confirmation. Then skip to section 5 (Summarize Scope Decisions) with:
90
90
 
91
91
  **If `source_type: "source"` (default):** Continue to scope templates below.
92
92
 
93
- ### 2b. Offer Scope Templates
93
+ ### 2b. Confirm Supplemental Documentation (if doc_urls collected)
94
+
95
+ **If `source_type: "source"` AND supplemental `doc_urls` were collected in step 01:**
96
+
97
+ "**Supplemental documentation URLs:**
98
+ {numbered list of collected doc_urls with labels}
99
+
100
+ These will be included as T3 external references in the skill brief.
101
+ Add, remove, or confirm these URLs."
102
+
103
+ Wait for confirmation. Record any changes to `doc_urls`.
104
+
105
+ **If no supplemental doc_urls were collected:** Skip this subsection.
106
+
107
+ ### 2c. Offer Scope Templates
94
108
 
95
109
  Load `{scopeTemplatesFile}` for the scope type options ([F], [M], [P]) and their descriptions.
96
110
 
@@ -153,8 +167,8 @@ Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Conti
153
167
 
154
168
  #### Menu Handling Logic:
155
169
 
156
- - IF A: Execute {advancedElicitationTask}, and when finished redisplay the menu
157
- - IF P: Execute {partyModeWorkflow}, and when finished redisplay the menu
170
+ - IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
171
+ - IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
158
172
  - IF C: Load, read entire file, then execute {nextStepFile}
159
173
  - IF Any other comments or queries: help user respond then [Redisplay Menu Options](#6-present-menu-options)
160
174
 
@@ -5,8 +5,8 @@ description: 'Present complete skill brief for user review and confirmation befo
5
5
  nextStepFile: './step-05-write-brief.md'
6
6
  reviseStepFile: './step-03-scope-definition.md'
7
7
  briefSchemaFile: '../data/skill-brief-schema.md'
8
- advancedElicitationTask: '{project-root}/_bmad/core/workflows/advanced-elicitation/workflow.xml'
9
- partyModeWorkflow: '{project-root}/_bmad/core/workflows/party-mode/workflow.md'
8
+ advancedElicitationSkill: '/bmad-advanced-elicitation'
9
+ partyModeSkill: '/bmad-party-mode'
10
10
  ---
11
11
 
12
12
  # Step 4: Confirm Brief
@@ -78,6 +78,11 @@ Compile all gathered data from steps 01-03 into the complete brief:
78
78
  - **scope.include:** {include patterns from step 03}
79
79
  - **scope.exclude:** {exclude patterns from step 03}
80
80
  - **scope.notes:** {any scope notes from step 03}
81
+ - **source_type:** {source or docs-only, from step 01}
82
+ - **doc_urls:** {collected documentation URLs with labels, from steps 01/03 — include if source_type is "docs-only" or supplemental URLs were collected}
83
+ - **scripts_intent:** {detect/none/description from step 03, or "detect" if not explicitly set}
84
+ - **assets_intent:** {detect/none/description from step 03, or "detect" if not explicitly set}
85
+ - **source_authority:** {official/community/internal from step 01 — default "community"}
81
86
 
82
87
  ### 3. Present Brief for Review
83
88
 
@@ -101,6 +106,21 @@ Scope: {scope.type}
101
106
  Exclude: {scope.exclude patterns, one per line}
102
107
  Notes: {scope.notes}
103
108
 
109
+ {If source_type is "docs-only":}
110
+ Source Type: docs-only
111
+ Doc URLs:
112
+ {doc_urls, one per line with labels}
113
+
114
+ {If source_type is "source" AND supplemental doc_urls collected:}
115
+ Supplemental Docs:
116
+ {doc_urls, one per line with labels}
117
+
118
+ {If scripts_intent or assets_intent was explicitly set (not default "detect"):}
119
+ Scripts: {scripts_intent}
120
+ Assets: {assets_intent}
121
+
122
+ Source Authority: {source_authority}
123
+
104
124
  Version: {version}
105
125
  Created: {created}
106
126
  Created by: {created_by}
@@ -145,8 +165,8 @@ Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Par
145
165
  #### Menu Handling Logic:
146
166
 
147
167
  - IF R: Load, read entire file, then execute {reviseStepFile} to re-enter scope definition
148
- - IF A: Execute {advancedElicitationTask}, and when finished redisplay the menu
149
- - IF P: Execute {partyModeWorkflow}, and when finished redisplay the menu
168
+ - IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
169
+ - IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
150
170
  - IF C: Load, read entire file, then execute {nextStepFile}
151
171
  - IF Any other comments or queries: help user respond, apply any field adjustments, re-present brief if changed, then [Redisplay Menu Options](#6-present-menu-options)
152
172
 
@@ -87,19 +87,36 @@ scope:
87
87
  exclude:
88
88
  - "{approved exclude patterns}"
89
89
  notes: "{approved scope notes or empty string}"
90
- # Include doc_urls when source_type is "docs-only" or when supplemental doc URLs were provided
91
- # doc_urls:
92
- # - url: "{documentation URL}"
93
- # label: "{page label}"
94
- # scripts_intent: detect # Optional: detect | none | description of expected scripts
95
- # assets_intent: detect # Optional: detect | none | description of expected assets
96
- # source_authority: community # Optional: official | community | internal
97
90
  ---
98
91
  ```
99
92
 
100
- **If `source_type: "docs-only"`:** Include `doc_urls` array (required). `source_repo` may be set to the doc site URL for reference or omitted. `source_authority` must be `community`.
93
+ **Conditional optional field inclusion:**
101
94
 
102
- **If `doc_urls` were collected during scope definition (supplemental mode):** Include the `doc_urls` array even when `source_type: "source"`.
95
+ **If `source_type: "docs-only"` OR supplemental `doc_urls` were collected:**
96
+ Include the `doc_urls` array (uncommented) in the generated YAML:
97
+ ```yaml
98
+ doc_urls:
99
+ - url: "{documentation URL}"
100
+ label: "{page label}"
101
+ ```
102
+ When `source_type: "docs-only"`: `doc_urls` is required (at least one entry), `source_repo` may be set to the doc site URL for reference or omitted.
103
+
104
+ **If `scripts_intent` was collected and is not the default `"detect"`:**
105
+ Include the `scripts_intent` field (uncommented):
106
+ ```yaml
107
+ scripts_intent: "{none or description}"
108
+ ```
109
+
110
+ **If `assets_intent` was collected and is not the default `"detect"`:**
111
+ Include the `assets_intent` field (uncommented):
112
+ ```yaml
113
+ assets_intent: "{none or description}"
114
+ ```
115
+
116
+ **Always include** `source_authority` (default: `"community"`, forced to `"community"` when `source_type: "docs-only"`):
117
+ ```yaml
118
+ source_authority: "{official|community|internal}"
119
+ ```
103
120
 
104
121
  ### 4. Write the File
105
122
 
@@ -128,6 +145,13 @@ qmd collection add {forge_data_folder}/{skill-name} --name {skill-name}-brief --
128
145
  qmd embed
129
146
  ```
130
147
 
148
+ **Embed verification:**
149
+
150
+ After `qmd embed` completes, verify the collection was embedded:
151
+ - Run `qmd status` or `qmd collection list` and confirm `{skill-name}-brief` shows document count > 0
152
+ - If verification succeeds: proceed to registry update normally
153
+ - If verification fails: log warning "QMD embed verification failed for {skill-name}-brief — collection may not be searchable yet", still proceed to registry update but add `status: "pending"` field to the registry entry
154
+
131
155
  **Registry update:**
132
156
 
133
157
  Read `{sidecar_path}/forge-tier.yaml` and update the `qmd_collections` array.
@@ -140,6 +164,7 @@ If an entry with `name: "{skill-name}-brief"` already exists, replace it. Otherw
140
164
  source_workflow: "brief-skill"
141
165
  skill_name: "{skill-name}"
142
166
  created_at: "{current ISO date}"
167
+ # status: "pending" # Added only when embed verification fails
143
168
  ```
144
169
 
145
170
  Write the updated forge-tier.yaml.
@@ -51,7 +51,7 @@ installed_path: '{project-root}/_bmad/skf/workflows/skillforge/brief-skill'
51
51
 
52
52
  Load and read full config from {project-root}/_bmad/skf/config.yaml and resolve:
53
53
 
54
- - `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`, `forge_data_folder`, `sidecar_path`
54
+ - `project_name`, `output_folder`, `user_name`, `communication_language`, `forge_data_folder`, `sidecar_path`
55
55
 
56
56
  ### 2. First Step Execution
57
57
 
@@ -116,6 +116,7 @@ Do NOT repeat Tier 1's name/purpose/key-params table format in Tier 2. Tier 2 is
116
116
  4. If Tier 1 alone exceeds 300 lines, reduce Key API Summary and Architecture at a Glance
117
117
  5. Tier 1 sections are kept short enough that `split-body` targets the larger Tier 2 sections (`## Full ...` headings) instead
118
118
  6. After split-body, SKILL.md must still contain all Tier 1 sections with actionable content
119
+ 7. **Signature fidelity:** When populating function entries from the extraction inventory, always use the `params` and `return_type` fields from the provenance-map entry (T1/AST-backed). Do not substitute parameter names, types, or return types from documentation, README examples, or enrichment annotations. T2/T3 sources may enrich descriptions and add usage notes, but structural signature data from AST extraction is authoritative.
119
120
 
120
121
  ### Reference File Rules
121
122
 
@@ -51,7 +51,7 @@ Structural extraction via ast-grep — verified exports with line-level citation
51
51
  - Internal/private functions: excluded (not part of public API)
52
52
 
53
53
  ### ast-grep Patterns
54
- - JS/TS: `export function $NAME($$$PARAMS): $RET` / `export const $NAME`
54
+ - JS/TS: `export function $NAME($$$PARAMS): $RET` / `export const $NAME = ($$$PARAMS) => $BODY` / `export const $NAME` / `export class $NAME`
55
55
  - Rust: `pub fn $NAME($$$PARAMS) -> $RET`
56
56
  - Python: function definitions within `__all__` list
57
57
  - Go: capitalized function definitions
@@ -168,12 +168,19 @@ find_code_by_rule(
168
168
 
169
169
  When MCP tools are unavailable or the repo exceeds 500 files in scope, use `--json=stream` (NEVER `--json` or `--json=pretty`) with line-by-line Python processing:
170
170
 
171
+ **Head cap selection:** The `| head -N` cap at the end of the pipeline controls how many exports are captured. Select `N` based on scope and tier:
172
+ - **Default (Quick/Forge, any scope):** `N = 200`
173
+ - **Forge+/Deep with `scope.type: "full-library"`:** `N = 500`
174
+
175
+ For full-library skills at higher tiers, the larger cap prevents silently dropping internal module exports that maintainers need. The cap is applied AFTER exclude-pattern filtering, so useful results are not wasted on excluded files.
176
+
171
177
  ```bash
172
178
  # Note: use $$$ for variadic params in ast-grep patterns (e.g., 'def $NAME($$$PARAMS)')
173
179
  # {exclude_patterns} = Python list from brief's scope.exclude, e.g. ['tests/**', '**/test_*']
174
180
  # If scope.exclude is absent or empty in the brief, inject [] as the default.
175
181
  # Patterns are matched against the full file path as emitted by ast-grep.
176
182
  # Ensure paths are relative to the same root as the patterns (strip ./ prefix if needed).
183
+ # {HEAD_CAP} = 200 (default) or 500 (Forge+/Deep full-library) — see head cap selection above.
177
184
  ast-grep -p '{pattern}' -l {language} --json=stream {path} | python3 -c "
178
185
  import sys, json, fnmatch, signal
179
186
  signal.signal(signal.SIGPIPE, signal.SIG_DFL)
@@ -193,7 +200,7 @@ for line in sys.stdin:
193
200
  sig = m.get('text','').split(chr(10))[0].strip()
194
201
  print(f'[AST:{f}:L{ln}] {sig}')
195
202
  except: pass
196
- " | head -200
203
+ " | head -{HEAD_CAP}
197
204
  ```
198
205
 
199
206
  **Critical constraints:**
@@ -201,7 +208,7 @@ for line in sys.stdin:
201
208
  - ALWAYS use `--json=stream` — never `--json` (loads entire array into memory)
202
209
  - ALWAYS process line-by-line (`for line in sys.stdin`) — never `json.load(sys.stdin)`
203
210
  - ALWAYS cap output with `| head -N` as a safety valve
204
- - For repos > 500 files, process in directory batches of 20-50 files each
211
+ - For repos > 500 files, process in directory batches of 20-50 files each: split by top-level source directory, run the CLI streaming template per batch with the same head cap, then merge results and deduplicate by export name (keep the first occurrence if duplicates exist across batches)
205
212
 
206
213
  ### YAML Rule Recipes by Language
207
214
 
@@ -253,6 +260,32 @@ rule:
253
260
  pattern: 'export const $NAME = $VALUE'
254
261
  ```
255
262
 
263
+ **JavaScript/TypeScript — exported arrow functions:**
264
+
265
+ ```yaml
266
+ id: js-exported-arrow-functions
267
+ language: typescript
268
+ rule:
269
+ pattern: 'export const $NAME = ($$$PARAMS) => $BODY'
270
+ ```
271
+
272
+ > **JS/TS Pattern Merging:** Modern TypeScript codebases often use `export const` exclusively for all exports (arrow functions, objects, constants). Run ALL four JS/TS patterns (functions, arrow functions, constants, classes) and merge results by `$NAME`. Priority when deduplicating: arrow function match > function declaration match > constant match. Arrow function matches capture parameters directly; constant matches require inspecting `$VALUE` to extract signatures.
273
+
274
+ **JavaScript/TypeScript — exported classes (use `find_code`, not `find_code_by_rule`):**
275
+
276
+ ```yaml
277
+ id: js-exported-classes
278
+ language: typescript
279
+ rule:
280
+ pattern: 'export class $NAME'
281
+ ```
282
+
283
+ > **Important:** For class patterns, use `find_code()` rather than `find_code_by_rule()`. The `find_code_by_rule` API requires explicit AST `kind` rules for class exports, which adds complexity. The simpler `find_code()` pattern approach works reliably for class detection.
284
+
285
+ **JavaScript/TypeScript — re-export detection (use `find_code`):**
286
+
287
+ Use `find_code()` with pattern `export { $$$NAMES } from $SOURCE` for re-export detection. Note: this pattern may produce multiple AST node matches. Post-process results to split comma-separated names from `$$$NAMES`. For complex re-export chains (aliased exports, default re-exports, namespace re-exports), fall back to the Re-Export Tracing protocol in `extraction-patterns-tracing.md`.
288
+
256
289
  **Rust — public functions:**
257
290
 
258
291
  ```yaml
@@ -270,12 +303,29 @@ rule:
270
303
  id: go-exported-functions
271
304
  language: go
272
305
  rule:
273
- pattern: 'func $NAME($$$PARAMS) $RET'
306
+ any:
307
+ - pattern: 'func $NAME($$$PARAMS) $RET'
308
+ - pattern: 'func $NAME($$$PARAMS)'
274
309
  constraints:
275
310
  NAME:
276
311
  regex: '^[A-Z]'
277
312
  ```
278
313
 
314
+ ### Known ast-grep Limitations
315
+
316
+ When using ast-grep for extraction, be aware of these documented limitations:
317
+
318
+ 1. **`find_code_by_rule` requires explicit `kind` for class exports:** The `export class $NAME` pattern needs a `kind` rule specifying the tree-sitter node type when used with `find_code_by_rule`. Use the simpler `find_code()` API instead for class detection.
319
+
320
+ 2. **Re-export patterns produce multiple AST nodes:** `export { A, B, C } from './module'` decomposes into multiple metavariable bindings for `$$$NAMES`. Results require post-processing to split comma-separated names.
321
+
322
+ 3. **Default anonymous exports capture no name:** `export default function $NAME` works, but `export default $EXPR` (anonymous default export) captures no name in `$NAME`. Fall back to source reading (T1-low) for anonymous defaults.
323
+
324
+ 4. **Fallback protocol:** If an ast-grep pattern returns errors or zero results when results are expected:
325
+ - First: retry with `find_code()` using a simpler pattern (drop type annotations, use broader match)
326
+ - Second: if `find_code()` also fails, fall back to source reading for that pattern category (T1-low confidence)
327
+ - Never silently accept zero results for a pattern category that the source language commonly uses
328
+
279
329
  ### Re-Export Tracing and Script/Asset Extraction
280
330
 
281
331
  See `extraction-patterns-tracing.md` for:
@@ -199,7 +199,8 @@ Each reference file includes:
199
199
  "source_line": 42,
200
200
  "confidence": "T1",
201
201
  "extraction_method": "ast-grep",
202
- "ast_node_type": "export_function_declaration"
202
+ "ast_node_type": "export_function_declaration",
203
+ "signature_source": "T1"
203
204
  }
204
205
  ],
205
206
  "file_entries": [
@@ -102,7 +102,7 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
102
102
 
103
103
  After checkout, apply the original glob `include_patterns` as file-level filters when building the extraction file list — sparse-checkout gets the right directories, glob filtering narrows to the exact files. When `--no-cone` mode was used, most exclude filtering is already done at the git level, but apply `exclude_patterns` as a final pass to catch any edge cases where gitignore pattern matching diverges from the brief's glob semantics. Always-included root files (see above) are exempt from post-checkout filtering.
104
104
 
105
- 3. **If clone succeeds:** Update the working source path to `{temp_path}` for all subsequent AST operations in this step. Proceed with the **Forge/Deep Tier** extraction strategy below. Mark `ephemeral_clone_active = true` for cleanup.
105
+ 3. **If clone succeeds:** Update the working source path to `{temp_path}` for all subsequent AST operations in this step. Capture the source commit: `git -C {temp_path} rev-parse HEAD` — store as `source_commit` in context. Proceed with the **Forge/Deep Tier** extraction strategy below. Mark `ephemeral_clone_active = true` for cleanup.
106
106
 
107
107
  4. **If clone fails (network error, auth failure, timeout):**
108
108
 
@@ -116,6 +116,20 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
116
116
 
117
117
  ---
118
118
 
119
+ ## Source Commit Capture (all tiers, source mode only)
120
+
121
+ **If `source_type: "docs-only"`:** skip — set `source_commit: null`.
122
+
123
+ After the source path is accessible, capture the current commit hash for provenance tracking:
124
+
125
+ - **Local path:** `git -C {source_root} rev-parse HEAD` — if the path is a git repo
126
+ - **Ephemeral clone (Forge/Deep):** already captured during clone (step 3 above)
127
+ - **Quick tier (remote, no clone):** `gh api repos/{owner}/{repo}/commits/{branch} --jq '.sha'`
128
+
129
+ Store the result as `source_commit` in context. If capture fails (not a git repo, API unavailable), set `source_commit: null` — this is not an error.
130
+
131
+ ---
132
+
119
133
  ## Version Reconciliation (all tiers, source mode only)
120
134
 
121
135
  **If `source_type: "docs-only"`:** skip this section — no source files exist to reconcile.
@@ -111,16 +111,16 @@ Halt with specific error: "Brief validation failed: missing required field `{fie
111
111
 
112
112
  ### 4. Resolve Source Code Location
113
113
 
114
- **If `source_type: "docs-only"`:** Skip source resolution. Set `source_location: null` in context. Proceed directly to section 5 (Report Initialization) — docs-only skills have no source to resolve.
114
+ **If `source_type: "docs-only"`:** Skip source resolution. Set `source_root: null` in context. Proceed directly to section 5 (Report Initialization) — docs-only skills have no source to resolve.
115
115
 
116
116
  **If source_repo is a GitHub URL or owner/repo format:**
117
117
  - Verify repository exists via `gh_bridge.list_tree(owner, repo, branch)` — **Tool resolution:** `gh api repos/{owner}/{repo}/git/trees/{branch}?recursive=1` or direct file listing if local; see [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md)
118
118
  - If branch not specified, detect default branch
119
- - Store resolved: owner, repo, branch, file tree
119
+ - Store resolved: owner, repo, branch, file tree — note: `source_root` for remote repos is set to the ephemeral clone path during extraction (step-03)
120
120
 
121
121
  **If source_repo is a local path:**
122
122
  - Verify path exists and contains source files
123
- - Store resolved: local path, file listing
123
+ - Store resolved: local path as `source_root`, file listing
124
124
 
125
125
  **If source cannot be resolved:**
126
126
  Halt with: "Source not found: `{source_repo}`. Verify the repository exists and is accessible."
@@ -170,7 +170,7 @@ ONLY WHEN forge-tier.yaml is loaded, skill-brief.yaml is validated, and source c
170
170
 
171
171
  - Forge tier loaded from sidecar with tool availability
172
172
  - Skill brief loaded and all required fields validated
173
- - Source code location resolved and accessible (or `source_location: null` confirmed for docs-only skills)
173
+ - Source code location resolved and accessible (or `source_root: null` confirmed for docs-only skills)
174
174
  - Initialization summary displayed with tier and capabilities
175
175
  - Auto-proceeded to step-02
176
176
 
@@ -47,7 +47,7 @@ For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed
47
47
 
48
48
  - Available: tier, tools.ccc, ccc_index from forge-tier.yaml (loaded in step-01)
49
49
  - Available: brief_data (name, scope, description) from step-01
50
- - Available: source_root (resolved source path) from step-01
50
+ - Available: source_root (resolved source path) from step-01 — note: for remote sources, `source_root` may not be a local path until step-03 performs the ephemeral clone
51
51
  - Focus: Semantic discovery only — no extraction, no compilation
52
52
  - Dependencies: step-02 must have completed
53
53
 
@@ -67,11 +67,13 @@ Check `tools.ccc` from forge-tier.yaml. If `tools.ccc` is false, set `{ccc_disco
67
67
 
68
68
  If `tools.ccc` is true, continue to section 2.
69
69
 
70
+ **Remote source guard:** If `source_root` is a remote URL (GitHub repository not yet cloned locally — ephemeral clone happens in step-03), CCC cannot operate. Set `{ccc_discovery: []}` and display: "CCC discovery skipped — remote source not yet available locally. CCC requires a local source index." Auto-proceed to the step completion / next step section.
71
+
70
72
  ### 2. Check CCC Index State
71
73
 
72
74
  Read `ccc_index` from forge-tier.yaml:
73
75
 
74
- - If `ccc_index.status` is `"fresh"`: continue to section 3.
76
+ - If `ccc_index.status` is `"fresh"` or `"created"`: continue to section 3.
75
77
  - If `ccc_index.status` is `"stale"`: display brief note — "CCC index is stale — discovery results may miss recent changes." Continue to section 3.
76
78
  - If `ccc_index.status` is `"none"` or `"failed"`: attempt lazy indexing via `ccc_bridge.ensure_index(source_root)`. If indexing succeeds, continue to section 3. If indexing fails, set `{ccc_discovery: []}` and auto-proceed to section 5.
77
79
 
@@ -46,7 +46,7 @@ To extract all public exports, function signatures, type definitions, and co-imp
46
46
 
47
47
  ## CONTEXT BOUNDARIES:
48
48
 
49
- - Available: brief_data, tier, source_location, file_tree from step-01; ecosystem check outcome from step-02 (proceed/halt decision — no named variable stored)
49
+ - Available: brief_data, tier, source_root, file_tree from step-01; ecosystem check outcome from step-02 (proceed/halt decision — no named variable stored)
50
50
  - Focus: Source code extraction and inventory building
51
51
  - Limits: Do NOT compile, assemble, or write any output
52
52
  - Dependencies: Source code must be accessible (resolved in step-01)
@@ -57,11 +57,26 @@ To fetch temporal context (issues, PRs, changelogs, release notes) from the sour
57
57
  Evaluate the following conditions sequentially. **If ANY condition fails, skip silently to section 5 (auto-proceed) with no output:**
58
58
 
59
59
  1. **Tier is Deep:** If tier is Quick, Forge, or Forge+, skip silently.
60
- 2. **Source is GitHub:** Verify `source_repo` is a GitHub URL (`https://github.com/...`) or `owner/repo` format. If the source is a local path, a non-GitHub URL, or any other format, skip silently.
60
+ 2. **Source is GitHub:** Verify `source_repo` is a GitHub URL (`https://github.com/...`) or `owner/repo` format. If the source is a local path, a non-GitHub URL, or any other format, attempt GitHub remote detection (section 1b) before skipping.
61
61
  3. **`gh` CLI is available:** Run `gh auth status` to verify the CLI is installed and authenticated. If it fails, skip silently.
62
62
 
63
63
  All three conditions must pass to proceed to section 2.
64
64
 
65
+ ### 1b. GitHub Remote Detection for Local Sources
66
+
67
+ **Only runs when condition 2 above fails because `source_repo` is a local path.**
68
+
69
+ Local repositories that are clones of GitHub repos contain temporal context (issues, PRs, releases) accessible via `gh`. Detect this automatically:
70
+
71
+ 1. Check if the local path is a git repository: `git -C {source_repo} rev-parse --is-inside-work-tree`
72
+ 2. If not a git repo: skip silently to section 5 (current behavior).
73
+ 3. Extract the origin remote: `git -C {source_repo} remote get-url origin`
74
+ 4. If the remote URL contains `github.com`:
75
+ - Extract `owner/repo` from the remote URL (strip `.git` suffix, handle both HTTPS and SSH formats)
76
+ - Log: "**Local source with GitHub remote detected:** {owner}/{repo} — fetching temporal context."
77
+ - Use the extracted `owner/repo` for all `gh` API calls in sections 3-4. Continue to condition 3 (gh CLI check).
78
+ 5. If no remote, or remote is not GitHub: skip silently to section 5 (current behavior).
79
+
65
80
  ### 2. Check Cache (Skip If Fresh)
66
81
 
67
82
  Read `forge-tier.yaml` from the sidecar path.
@@ -157,7 +172,7 @@ If a `{skill-name}-temporal` collection already exists, remove and recreate for
157
172
 
158
173
  ```bash
159
174
  qmd collection remove {skill-name}-temporal
160
- qmd collection add _bmad-output/{skill-name}-temporal/ --name {skill-name}-temporal --mask "*.md"
175
+ qmd collection add {project-root}/_bmad-output/{skill-name}-temporal/ --name {skill-name}-temporal --mask "*.md"
161
176
  qmd embed
162
177
  ```
163
178
 
@@ -178,7 +193,7 @@ If an entry with `name: "{skill-name}-temporal"` already exists in `qmd_collecti
178
193
  **Clean up** the staging directory after successful indexing:
179
194
 
180
195
  ```bash
181
- rm -rf _bmad-output/{skill-name}-temporal/
196
+ rm -rf {project-root}/_bmad-output/{skill-name}-temporal/
182
197
  ```
183
198
 
184
199
  **Error handling:**
@@ -85,14 +85,20 @@ Content fetched from external URLs is classified as **T3** (external, untrusted)
85
85
 
86
86
  After fetching a URL, apply the following heuristic to detect documentation root pages that contain no useful API content. This is common with modern documentation sites (Mintlify, Docusaurus, ReadTheDocs, GitBook) that render API content on subpages.
87
87
 
88
- **Root page detection heuristic — apply only when the URL path ends in `/`, `/index`, `/index.html`, has no path component (bare domain), or has 1 path segment (e.g., `/docs`). For deeper URL paths (2+ segments like `/api/reference`), skip this heuristic and keep the content as-is.**
88
+ **Root page detection — apply only when the URL path ends in `/`, `/index`, `/index.html`, has no path component (bare domain), or has 1 path segment (e.g., `/docs`). For deeper URL paths (2+ segments like `/api/reference`), skip this heuristic and keep the content as-is.**
89
89
 
90
- **A page is classified as a root if BOTH conditions are true:**
90
+ Subpage discovery is triggered if **either** of the following independent triggers fires:
91
+
92
+ **Trigger 1 — Content-based (both conditions must be true):**
91
93
 
92
94
  1. **Zero API content indicators:** The fetched markdown contains none of: fenced code blocks (`` ``` ``), parameter tables (`|---|`), or function signature patterns (`def `, `function `, `fn `, `func `, `export `).
93
95
  2. **High link density:** More than 70% of non-empty lines are markdown links (matching `[text](url)` with no other substantive content on the line).
94
96
 
95
- If only one condition is true, treat the page as having partial content keep it as-is and do NOT trigger subpage discovery.
97
+ **Trigger 2URL-based (independent of content analysis):**
98
+
99
+ The URL matches the path criteria above (ends in `/`, bare domain, or 1 segment) AND the fetched content is under **2000 words**. Short content on root-like URLs almost certainly indicates a navigation hub or landing page, even if it contains introductory code examples that would prevent Trigger 1 from firing. This handles modern doc sites (Mintlify, Docusaurus, GitBook) that include hero sections with code snippets on their root pages.
100
+
101
+ If neither trigger fires, keep the page content as-is and do NOT trigger subpage discovery.
96
102
 
97
103
  **If a root URL with minimal content is detected:**
98
104
 
@@ -147,7 +153,7 @@ Parse the successfully fetched markdown for:
147
153
  **If tier is Deep and at least one URL was fetched successfully:**
148
154
 
149
155
  1. Write fetched markdown files to a staging directory: `_bmad-output/{skill-name}-docs/`
150
- 2. Index into QMD: `qmd collection add _bmad-output/{skill-name}-docs/ --name {skill-name}-docs --mask "*.md"`
156
+ 2. Index into QMD: `qmd collection add {project-root}/_bmad-output/{skill-name}-docs/ --name {skill-name}-docs --mask "*.md"`
151
157
  3. Generate embeddings: `qmd embed` (required for `vector_search` and `deep_search`)
152
158
  4. Register in forge-tier.yaml `qmd_collections` array:
153
159
 
@@ -159,7 +165,7 @@ Parse the successfully fetched markdown for:
159
165
  created_at: "{current ISO date}"
160
166
  ```
161
167
 
162
- 5. Clean up staging directory after indexing: `rm -rf _bmad-output/{skill-name}-docs/`
168
+ 5. Clean up staging directory after indexing: `rm -rf {project-root}/_bmad-output/{skill-name}-docs/`
163
169
 
164
170
  **If QMD indexing fails:** Warn: "QMD indexing of fetched docs failed. T3 items are still in the extraction inventory — enrichment will proceed without QMD-indexed docs." Continue.
165
171
 
@@ -59,6 +59,17 @@ To assemble the complete skill content from the extraction inventory and enrichm
59
59
 
60
60
  Load `{skillSectionsData}` and `{assemblyRulesData}` completely. These define the agentskills.io-compliant format and detailed assembly rules for all output artifacts.
61
61
 
62
+ ### 1b. Signature Fidelity Rule
63
+
64
+ **When assembling function signatures, parameter lists, and return types in any SKILL.md section or reference file:**
65
+
66
+ - **T1 provenance-map entries (AST-extracted) are authoritative** for: function name, parameter names, parameter types, parameter order, return type, and optionality markers (e.g., `?`, `Optional`, `= default`).
67
+ - **T2 (QMD-enriched) and T3 (doc-derived) sources may ADD** contextual descriptions, usage notes, behavioral documentation, and examples to function entries, but **MUST NOT REPLACE** structural signature data from T1 entries.
68
+ - **On conflict:** If a T2/T3 source provides a different signature than the T1 extraction for the same export (e.g., different parameter count, different types, missing `Partial<>` wrapper), keep the T1 signature and log a warning in the evidence report: "Signature conflict for `{export_name}`: T1 shows `{t1_signature}`, T2/T3 shows `{other_signature}`. T1 used as authoritative."
69
+ - **`signature_source` field:** Record `signature_source: "T1" | "T1-low" | "T2" | "T3"` in each provenance-map entry to indicate the highest-confidence tier that contributed the structural signature data (params, return_type). This enables test-skill to verify signature provenance.
70
+
71
+ This rule applies to ALL sections including Tier 1 Key API Summary, Tier 2 Full API Reference, and Section 4b Migration & Deprecation Warnings.
72
+
62
73
  ### 2. Build SKILL.md Content
63
74
 
64
75
  Assemble each section in order using the assembly rules data file (`{assemblyRulesData}`). The data file specifies frontmatter format, Tier 1 section details (Sections 1-8, including conditional Section 7b for scripts/assets), Tier 2 section details (Sections 9-11), and assembly ordering rules. Follow it exactly. Assemble Section 7b (Scripts & Assets) only if `scripts_inventory` or `assets_inventory` is non-empty.
@@ -92,7 +103,7 @@ Following the structure from the skill-sections data file:
92
103
  - Set `source_commit` from resolved source (if available)
93
104
  - Set `stats` from extraction aggregate counts:
94
105
  - `exports_documented`: count of exports with documentation in the assembled SKILL.md
95
- - `exports_public_api`: count of exports from public entry points (`__init__.py`, `index.ts`, `lib.rs`, or equivalent)
106
+ - `exports_public_api`: count of exports from public entry points (`__init__.py`, `index.ts`, `lib.rs`, or equivalent) — derive this from step-03's entry-point validation (section 4b), NOT from the provenance-map entry count (which may be incomplete if extraction patterns missed some export types)
96
107
  - `exports_internal`: count of all other non-underscore-prefixed exports (internal modules, helpers, adapters)
97
108
  - `exports_total`: `exports_public_api` + `exports_internal`
98
109
  - `public_api_coverage`: `exports_documented / exports_public_api` (1.0 when all public API exports are documented; `null` if `exports_public_api` is 0)
@@ -100,7 +111,12 @@ Following the structure from the skill-sections data file:
100
111
  - Set `description` from the SKILL.md frontmatter `description` field (already assembled in section 2)
101
112
  - Set `language` from source analysis (e.g., `"typescript"`, `"python"`) — use the primary language of the entry point file
102
113
  - Set `ast_node_count` from extraction stats if ast-grep was used (Forge/Deep tier), otherwise omit
103
- - Set `tool_versions` based on tier and available tools. Resolve `{skf_version}` from the installed module's `package.json` at `{project-root}/_bmad/skf/package.json`. If unresolvable there, fall back to `node -p "require('./node_modules/bmad-module-skill-forge/package.json').version"`. If still unresolvable, use `"unknown"` and add a warning to the evidence report. Never hardcode the version.
114
+ - Set `tool_versions` based on tier and available tools. Resolve `{skf_version}` using this resolution chain (try each in order, use the first that succeeds):
115
+ 1. `{project-root}/_bmad/skf/package.json` → read `.version` field
116
+ 2. `node -p "require('./node_modules/bmad-module-skill-forge/package.json').version"`
117
+ 3. `{project-root}/_bmad/skf/VERSION` → read plain text file (single line containing version string, written by the SKF installer)
118
+ 4. `"unknown"` (final fallback — add a warning to the evidence report)
119
+ Never hardcode the version.
104
120
  - Store `commit_short` = first 8 characters of `source_commit` (or `"unknown"` if unavailable) for use in step-08 report.
105
121
  - If `scripts_inventory` is non-empty, populate `scripts[]` array and set `stats.scripts_count`. If `assets_inventory` is non-empty, populate `assets[]` array and set `stats.assets_count`. Omit these fields entirely when inventories are empty.
106
122
 
@@ -116,7 +132,7 @@ Group functions logically by module, file, or functional area.
116
132
 
117
133
  ### 6. Build provenance-map.json Content
118
134
 
119
- One entry per extracted export: export_name, export_type, params[] (typed strings), return_type, source_file, source_line, confidence tier (T1/T1-low/T2), extraction_method, ast_node_type. If `scripts_inventory` or `assets_inventory` is non-empty, add `file_entries[]` with file_name, file_type, source_file, content_hash, confidence, extraction_method. See `{skillSectionsData}` for full schema.
135
+ One entry per extracted export: export_name, export_type, params[] (typed strings), return_type, source_file, source_line, confidence tier (T1/T1-low/T2), extraction_method, ast_node_type, signature_source ("T1"|"T1-low"|"T2"|"T3" — indicates which tier contributed the structural signature). If `scripts_inventory` or `assets_inventory` is non-empty, add `file_entries[]` with file_name, file_type, source_file, content_hash, confidence, extraction_method. See `{skillSectionsData}` for full schema.
120
136
 
121
137
  ### 7. Build evidence-report.md Content
122
138
 
@@ -76,6 +76,8 @@ This performs frontmatter validation, description quality checks, body limit enf
76
76
 
77
77
  **Context sync after --fix:** If `fixed[]` is non-empty (i.e., `--fix` modified files on disk), re-read the modified SKILL.md to update the in-context copy. Verify the re-read content matches expectations before proceeding. This prevents silent divergence between the in-context SKILL.md and the on-disk version that step-07 will use for artifact generation.
78
78
 
79
+ **Description preservation after --fix:** After re-reading the modified SKILL.md, compare the frontmatter `description` field against the original step-05 compiled description. If `--fix` replaced the description with a generic or truncated version, restore the original description to the on-disk file and update the in-context copy. The step-05 compiled description is authoritative — auto-fix tools must not replace trigger-optimized descriptions.
80
+
79
81
  **Note:** `skill-check` may return non-zero exit code even when `errorCount` is 0. Always rely on parsed JSON, not the shell exit code.
80
82
 
81
83
  - **Score ≥ 70:** Record "Schema: PASS (score: {score}/100)" in evidence-report
@@ -101,15 +103,17 @@ If fails: auto-fix (deterministic), re-validate once, record result. If passes:
101
103
 
102
104
  **If step 2 reported `body.max_lines` failure:**
103
105
 
104
- **Preferred approach — selective split:** Rather than a full `split-body`, identify which Tier 2 section(s) are largest (usually `## Full API Reference`) and manually extract only those to `references/`. Keep all Tier 1 content and smaller Tier 2 sections inline. This preserves agent accuracy inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval (per Vercel research). The rationale: inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval (per Vercel research).
106
+ **Description preservation:** Before any split operation, capture the current SKILL.md frontmatter `description` field. After the split completes, verify the `description` was not modified. If it was replaced with a generic placeholder, restore the original immediately.
105
107
 
106
- **If manual selective split is not feasible**, fall back to the automated full split:
108
+ **Mandatory approach selective split:** Identify Tier 2 sections by their `## Full` heading prefix (e.g., `## Full API Reference`, `## Full Type Definitions`, `## Full Integration Patterns`). Extract ONLY those sections to `references/`, starting with the largest. Keep ALL Tier 1 content and any smaller sections inline. Inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval (per Vercel research).
107
109
 
108
- ```bash
109
- npx skill-check split-body <staging-skill-dir> --write
110
- ```
110
+ **FORBIDDEN:** Running `npx skill-check split-body --write` without prior selective extraction. The `split-body --write` command extracts ALL `##` sections top-to-bottom, destroying Tier 1 inline content that the two-tier design depends on. This command is a LAST RESORT only after selective split has been attempted and proven insufficient.
111
+
112
+ **If selective split alone does not bring body under the limit** (rare — typically only occurs when Tier 1 itself exceeds 300 lines): reduce Tier 1 Key API Summary and Architecture at a Glance sections to fit within limits. Do NOT fall back to automated `split-body --write` to solve a Tier 1 sizing problem.
113
+
114
+ **Tier 1 preservation check:** After ANY split operation, verify that ALL of the following sections remain inline in SKILL.md (not moved to references/): Overview, Quick Start, Common Workflows, Key API Summary, Migration & Deprecation Warnings (if present), Key Types, Architecture at a Glance, CLI (if present), Scripts & Assets (if present), Manual Sections. If any Tier 1 section was moved to references/, restore it immediately and re-split targeting only Tier 2 sections.
111
115
 
112
- After any split (selective or full), verify that context snippet section anchors (`#quick-start`, `#key-types`) still resolve to headings in SKILL.md.
116
+ **Anchor validation and remediation:** After any split, verify that context-snippet section anchors (`#quick-start`, `#key-types`) still resolve to headings in SKILL.md. If an anchor no longer resolves (section was split out), restore that section to SKILL.md inline content — the context-snippet must always reference sections that exist in the main file.
113
117
 
114
118
  Then re-validate: `npx skill-check check <staging-skill-dir> --format json --no-security-scan`
115
119
 
@@ -43,7 +43,7 @@ To write all compiled content to disk — 4 deliverable files to `{skills_output
43
43
 
44
44
  ## CONTEXT BOUNDARIES:
45
45
 
46
- - Available: All compiled content from step-05, validation results from step-06
46
+ - Available: All compiled content from step-05, validation results from step-06, source_root from step-01 (needed for section 5b CCC registration)
47
47
  - Focus: File system operations — create directories, write files
48
48
  - Limits: Do NOT modify content during writing
49
49
  - Dependencies: All content must be compiled and validated in context
@@ -145,7 +145,7 @@ Generate metadata.json following the exact structure defined in {skillTemplateDa
145
145
  "tool_versions": {
146
146
  "ast_grep": null,
147
147
  "qmd": null,
148
- "skf": "{skf_version}"
148
+ "skf": "{skf_version}" // Resolution chain: _bmad/skf/package.json → npm require → _bmad/skf/VERSION → "unknown"
149
149
  },
150
150
  "stats": {
151
151
  "exports_documented": "{number of exports found}",
@@ -17,6 +17,21 @@
17
17
  **Structural Issues:** {count}
18
18
  ```
19
19
 
20
+ ## Coherence Analysis — Naive Mode: Reference Consistency (split-body)
21
+
22
+ Only rendered when `references/` directory exists alongside SKILL.md.
23
+
24
+ ```markdown
25
+ ### Reference Consistency (split-body)
26
+
27
+ | # | Reference File | Export | Issue | SKILL.md Line | Reference Line |
28
+ |---|---------------|--------|-------|---------------|---------------|
29
+ | {per-mismatch rows} |
30
+
31
+ **Exports Cross-Checked:** {count}
32
+ **Mismatches Found:** {count}
33
+ ```
34
+
20
35
  ## Coherence Analysis — Contextual Mode
21
36
 
22
37
  ```markdown
@@ -42,6 +42,18 @@ tessl evaluates SKILL.md body content only — it does not read `references/*.md
42
42
  - Score based on: structural completeness only
43
43
  - Weight redistribution: skipped categories' weights (Signature Accuracy 22% + Type Coverage 14%) redistributed proportionally to remaining active categories
44
44
 
45
+ ### Docs-Only Mode (Quick tier, all [EXT:...] citations)
46
+
47
+ When `docs_only_mode: true` is set by step-03 (indicating a Quick tier skill where all SKILL.md citations are `[EXT:...]` format with no local source code):
48
+
49
+ - **Signature Accuracy:** Not scored (no source to compare against)
50
+ - **Type Coverage:** Not scored (no source to compare against)
51
+ - **Weight redistribution:** Same as Quick tier — Signature Accuracy (22%) and Type Coverage (14%) weights redistributed proportionally to remaining active categories
52
+ - **Export Coverage basis:** Documentation completeness rather than source coverage. Score = (documented_items_with_complete_descriptions / total_documented_items) * 100. A "complete" item has: description, parameters (if function/method), and return type (if function/method).
53
+ - **Coherence:** Standard rules for the detected mode (naive or contextual) apply unchanged
54
+
55
+ This is functionally identical to Quick tier weight redistribution but with a different coverage denominator (self-consistency instead of source comparison).
56
+
45
57
  ### Forge Tier (ast-grep)
46
58
  - Export Coverage: AST-backed export comparison
47
59
  - Signature Accuracy: AST-verified signature matching
@@ -21,9 +21,15 @@ Before analysis, determine source access level. Walk through these states in ord
21
21
  Check if `{source_path}` (from metadata.json `source_root`) exists on disk. If yes → full analysis at detected tier (AST + signatures). Set `analysis_confidence: full`.
22
22
 
23
23
  **State 2 — Local absent, provenance-map exists:**
24
- Check `{forge_data_folder}/{skill_name}/provenance-map.json`. If present, use it as the baseline export inventory — each entry contains structured fields: `export_name`, `export_type`, `params[]`, `return_type`, `source_file`, `source_line`, `confidence`, and `ast_node_type`. Cross-reference against SKILL.md documented exports for name-matching and param-by-param coverage. Signature verification compares SKILL.md's documented params/return types against provenance-map entries directly. If remote reading tools are available (zread, deepwiki, gh API, or similar), supplement by reading the entry point file for live signature verification. Set `analysis_confidence: provenance-map`.
24
+ Check `{forge_data_folder}/{skill_name}/provenance-map.json`. If present AND contains at least 1 entry, use it as the baseline export inventory — each entry contains structured fields: `export_name`, `export_type`, `params[]`, `return_type`, `source_file`, `source_line`, `confidence`, and `ast_node_type`. Cross-reference against SKILL.md documented exports for name-matching and param-by-param coverage. Signature verification compares SKILL.md's documented params/return types against provenance-map entries directly.
25
25
 
26
- **State 2 limitations:** Signature verification at State 2 is **string comparison only**, not semantic. Provenance-map stores parameters as flat string arrays (e.g., `["data: Union[BinaryIO, list, str]"]`), so `str` vs `String` or `list` vs `List[Any]` would be treated as mismatches even when semantically equivalent. For full type-aware verification (handling type aliases, generic equivalence), State 1 (local source) with AST re-parsing is required. When the SKILL.md was compiled from the same provenance-map (typical for create-then-test flows), strings match exactly and this limitation has no practical effect.
26
+ **Cross-reference with metadata.json:** After loading provenance-map entries, compare the entry count against `metadata.json`'s `exports[]` array length and `stats.exports_public_api` count. If metadata reports more exports than provenance-map entries:
27
+ - Compute `gap = metadata.exports.length - provenance_map.entries.length`
28
+ - Report: "Provenance-map contains {pmap_count} entries but metadata.json lists {meta_count} exports ({gap} gap). Coverage denominator uses the union."
29
+ - Build the coverage denominator from the **union** of provenance-map entry names and metadata.json `exports[]` names. Exports present in metadata but absent from provenance-map are counted as "missing documentation" in the coverage calculation.
30
+ - If metadata.json is unavailable or has no `exports[]` array, use provenance-map count alone with a note: "Coverage denominator is provenance-map only — may undercount if extraction was incomplete." If remote reading tools are available (zread, deepwiki, gh API, or similar), supplement by reading the entry point file for live signature verification. Set `analysis_confidence: provenance-map`.
31
+
32
+ **State 2 limitations:** Signature verification at State 2 is **string comparison only**, not semantic. Provenance-map stores parameters as flat string arrays (e.g., `["data: Union[BinaryIO, list, str]"]`), so `str` vs `String` or `list` vs `List[Any]` would be treated as mismatches even when semantically equivalent. For full type-aware verification (handling type aliases, generic equivalence), State 1 (local source) with AST re-parsing is required. When the SKILL.md was compiled from the same provenance-map (typical for create-then-test flows), most strings will match. However, enrichment (step-04) and doc-fetching (step-03c) during compilation may alter parameter descriptions, add type annotations, or normalize signatures, causing mismatches even in create-then-test flows. Expect some string-level mismatches and treat them as compilation artifacts, not source drift signals, until signature fidelity is enforced by step-05's Signature Fidelity Rule (see `signature_source` field in provenance-map entries).
27
33
 
28
34
  **State 3 — No provenance-map, metadata exports exist (quick-skill path):**
29
35
  If no provenance-map.json exists (typical for quick-skill output), fall back to `metadata.json`'s `exports[]` array for the export name list. Coverage check becomes a self-consistency comparison: are all names in `exports[]` documented in SKILL.md with description, parameters, and return type? Signatures cannot be verified. If remote reading tools are available, supplement by reading the entry point for live export comparison. Set `analysis_confidence: metadata-only`.
@@ -87,6 +87,38 @@ Build the **documented inventory** — a list of everything the SKILL.md claims
87
87
 
88
88
  **Split-body traversal:** If a `references/` directory exists alongside SKILL.md and SKILL.md's `## Full` headings are absent or stubs (not a stack skill's structural references), extend the documented inventory scan to include all `references/*.md` files. After split-body, Tier 2 content (Full API Reference, Full Type Definitions) lives in reference files — the inventory must reflect the full skill content regardless of where it resides.
89
89
 
90
+ ### 1b. Cross-Check Split-Body Consistency
91
+
92
+ **Only execute if a `references/` directory exists alongside SKILL.md** (detected during split-body traversal in Section 1). Skip silently otherwise.
93
+
94
+ For each function, class, type, or interface that appears in BOTH the SKILL.md body AND any `references/*.md` file, compare the documented signatures:
95
+
96
+ - **Parameters:** name, type, order, optionality
97
+ - **Return types:** exact type match
98
+ - **Description:** no contradictions (brief vs detailed is acceptable; conflicting semantics is not)
99
+
100
+ **SKILL.md body is authoritative.** When a mismatch is found, the reference file is the one that needs updating.
101
+
102
+ Build a split-body consistency findings list:
103
+
104
+ ```json
105
+ {
106
+ "cross_check_mismatches": [
107
+ {
108
+ "export": "formatDate",
109
+ "skill_md_line": 42,
110
+ "reference_file": "references/api-reference.md",
111
+ "reference_line": 18,
112
+ "issue": "SKILL.md shows (date: Date) => string, reference shows (date: Date, format?: string) => string"
113
+ }
114
+ ],
115
+ "exports_cross_checked": 12,
116
+ "mismatches_found": 1
117
+ }
118
+ ```
119
+
120
+ Flag each mismatch as **High severity** — signature inconsistency between SKILL.md body and reference files undermines agent trust. These findings feed into the gap report (step-06).
121
+
90
122
  ### 2. Analyze Source Code (Tier-Dependent)
91
123
 
92
124
  Start from the package entry point (see 0b) and identify the public API surface. Then analyze those exports at the appropriate tier depth.
@@ -153,6 +185,8 @@ Load `{scoringRulesFile}` to determine category scores:
153
185
  - **Signature Accuracy:** (matching_signatures / total_documented) * 100 (Forge/Deep only, "N/A" for Quick)
154
186
  - **Type Coverage:** (documented_types / total_types) * 100 (Forge/Deep only, "N/A" for Quick)
155
187
 
188
+ **State 2 denominator validation:** When using provenance-map as the baseline (State 2), cross-reference the provenance-map entry count against `metadata.json`'s `exports[]` array before computing Export Coverage. If they diverge, use the union as the denominator per the source-access-protocol rules. Log the gap size if any.
189
+
156
190
  ### 5. Append Coverage Analysis to Output
157
191
 
158
192
  Append the **Coverage Analysis** section to `{outputFile}`:
@@ -181,11 +215,13 @@ Append the **Coverage Analysis** section to `{outputFile}`:
181
215
 
182
216
  ### Category Scores
183
217
 
184
- | Category | Score | Weight | Weighted |
185
- |----------|-------|--------|----------|
186
- | Export Coverage | {N}% | {weight}% | {weighted}% |
187
- | Signature Accuracy | {N}% | {weight}% | {weighted}% |
188
- | Type Coverage | {N}% | {weight}% | {weighted}% |
218
+ | Category | Score |
219
+ |----------|-------|
220
+ | Export Coverage | {N}% |
221
+ | Signature Accuracy | {N}% or N/A |
222
+ | Type Coverage | {N}% or N/A |
223
+
224
+ Note: Weight application is deferred to step-05 where all category weights are calculated after external validation availability is known.
189
225
  ```
190
226
 
191
227
  ### 6. Report Coverage Results
@@ -91,7 +91,27 @@ Build a simple structural findings list:
91
91
  }
92
92
  ```
93
93
 
94
- **After naive coherence → Skip to Section 6 (Append Results)**
94
+ **After naive coherence → Execute Section 2b if gate conditions met, then skip to Section 6 (Append Results)**
95
+
96
+ ### 2b. Migration/Deprecation Verification (Mode-Independent)
97
+
98
+ **Gate check:** Execute this section ONLY IF both conditions are met:
99
+ 1. Forge tier is **Deep** (tool-gated)
100
+ 2. `{forge_data_folder}/{skill_name}/evidence-report.md` exists (data-gated)
101
+
102
+ If either condition fails, skip silently and proceed to Section 6.
103
+
104
+ **This check runs regardless of naive/contextual mode.** T2-future annotations are a property of the source code and enrichment data, not the skill type.
105
+
106
+ Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Section 4b). Then check the skill's `evidence-report.md` (at `{forge_data_folder}/{skill_name}/evidence-report.md`) for T2-future annotation counts.
107
+
108
+ - **If T2-future annotations > 0 AND Section 4b is absent:** Flag as Medium severity gap: "Migration section missing — T2-future annotations exist but Section 4b is not present in SKILL.md Tier 1."
109
+ - **If T2-future annotations = 0 AND Section 4b is present:** Flag as Medium severity gap: "Migration section unexpected — Section 4b is present but no T2-future annotations were produced."
110
+ - **If evidence-report.md is unavailable:** Skip this check silently. Note: "Section 4b verification skipped — evidence-report.md not found."
111
+
112
+ Add findings to the coherence analysis results.
113
+
114
+ **After Section 2b (naive path) → Skip to Section 6 (Append Results)**
95
115
 
96
116
  ### 3. Contextual Mode: Extract References
97
117
 
@@ -121,6 +141,8 @@ DO NOT BE LAZY — For EACH reference found, launch a subprocess that:
121
141
 
122
142
  If subprocess unavailable, validate each reference in main thread.
123
143
 
144
+ 4. **Scripts/assets directory check:** If a `scripts/` or `assets/` directory exists alongside SKILL.md, verify that a "Scripts & Assets" section (Section 7b) is present in SKILL.md. This directory-level check applies in both modes (naive mode performs it in Section 2; contextual mode performs it here alongside per-reference validation). Flag absence as Medium severity gap per `{scoringRulesFile}`.
145
+
124
146
  ### 5. Contextual Mode: Check Integration Pattern Completeness
125
147
 
126
148
  For stack skills, verify integration patterns are complete:
@@ -146,11 +168,19 @@ Build integration completeness findings:
146
168
  }
147
169
  ```
148
170
 
149
- ### 5b. Contextual Mode + Deep Tier: Section 4b Verification
171
+ **Zero integration patterns:** If no integration patterns are documented in SKILL.md (e.g., a contextual-mode skill that uses shared types but has no middleware chains, plugin hooks, or event flows): record `patterns_documented: 0`, `patterns_complete: 0`. The coherence score will use reference validity alone — see `{scoringRulesFile}` Coherence Score Aggregation: "If no integration patterns exist, combined coherence equals reference validity."
150
172
 
151
- **Only execute if forge tier is Deep.** Skip silently for Quick/Forge tiers.
173
+ ### 5b. Migration/Deprecation Verification (Contextual Path)
152
174
 
153
- Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Section 4b). Then check the skill's `evidence-report.md` (at `{forge_data_folder}/{skill_name}/evidence-report.md`) for T2-future annotation counts.
175
+ **This section shares logic with Section 2b.** If you are on the contextual mode path (Sections 3-5), execute the migration check here using the same rules as Section 2b:
176
+
177
+ **Gate check:** Execute ONLY IF both conditions are met:
178
+ 1. Forge tier is **Deep** (tool-gated)
179
+ 2. `{forge_data_folder}/{skill_name}/evidence-report.md` exists (data-gated)
180
+
181
+ If either condition fails, skip silently.
182
+
183
+ Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Section 4b). Then check the skill's `evidence-report.md` for T2-future annotation counts.
154
184
 
155
185
  - **If T2-future annotations > 0 AND Section 4b is absent:** Flag as Medium severity gap: "Migration section missing — T2-future annotations exist but Section 4b is not present in SKILL.md Tier 1."
156
186
  - **If T2-future annotations = 0 AND Section 4b is present:** Flag as Medium severity gap: "Migration section unexpected — Section 4b is present but no T2-future annotations were produced."
@@ -158,6 +188,20 @@ Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Se
158
188
 
159
189
  Add findings to the coherence analysis results.
160
190
 
191
+ ### 5c. Calculate Coherence Scores
192
+
193
+ **Contextual mode only.** Calculate coherence percentages using the formulas defined in `{scoringRulesFile}` — Coherence Score Aggregation section:
194
+
195
+ ```
196
+ reference_validity = (valid_references / total_references) * 100
197
+ integration_completeness = (complete_patterns / total_patterns) * 100
198
+ combined_coherence = (reference_validity * 0.6) + (integration_completeness * 0.4)
199
+ ```
200
+
201
+ **Edge case:** If no integration patterns are documented (patterns_documented = 0), combined coherence equals reference validity alone. Do not divide by zero.
202
+
203
+ These values fill the `{percentage}%` placeholders in the output template loaded in Section 6.
204
+
161
205
  ### 6. Append Coherence Analysis to Output
162
206
 
163
207
  Load `{outputFormatsFile}` and use the appropriate Coherence Analysis section format (naive or contextual) to append findings to `{outputFile}`.
@@ -60,7 +60,15 @@ Read {outputFile} frontmatter to get the skill directory path (`skillDir`).
60
60
 
61
61
  Before running external validators, check if `{forge_data_folder}/{skill_name}/evidence-report.md` contains validation results (a `## Validation Results` section with quality scores).
62
62
 
63
- **Staleness check:** Compare the evidence report's generation date against SKILL.md's last-modified timestamp (or `metadata.json` `generation_date`). If SKILL.md was modified after the evidence report was generated (e.g., after update-skill), the cached results are stale — skip auto-reuse and proceed to section 2 for a fresh run.
63
+ **Staleness check:** Determine whether SKILL.md has changed since the evidence report was generated.
64
+
65
+ **Primary (git-tracked):** Run `git log -1 --format=%cI -- {skillDir}/SKILL.md` to get the last commit date of SKILL.md. Compare against the evidence report's generation date (from its frontmatter or the `## Validation Results` timestamp). If SKILL.md's last commit is newer, results are stale.
66
+
67
+ **Secondary (uncommitted changes):** Run `git diff --name-only -- {skillDir}/SKILL.md`. If output is non-empty, SKILL.md has uncommitted changes — treat results as stale regardless of commit dates.
68
+
69
+ **Fallback (non-git environments):** If git commands fail (not a git repository), fall back to `metadata.json` `generation_date` comparison with a warning: "Staleness check using metadata.json — may be identical to evidence report timestamp. Consider a git-tracked directory for reliable staleness detection."
70
+
71
+ If SKILL.md was modified after the evidence report was generated (e.g., after update-skill), the cached results are stale — skip auto-reuse and proceed to section 2 for a fresh run.
64
72
 
65
73
  If recent, non-stale results exist (from a create-skill run that just completed), auto-reuse them — skip re-running validators and use the existing scores. Record: "External validation: reused from create-skill evidence report." Skip to section 5 (append results).
66
74
 
@@ -64,6 +64,8 @@ Load `{scoringRulesFile}` to get:
64
64
  - Tier-dependent scoring adjustments
65
65
  - Any custom threshold override from workflow input
66
66
 
67
+ **Docs-only mode check:** If the Coverage Analysis section in `{outputFile}` notes docs-only mode (set by step-03 for skills with all `[EXT:...]` citations and no local source), apply Quick-tier weight redistribution: Signature Accuracy and Type Coverage are not scored, their weights (22% + 14%) are redistributed proportionally to remaining active categories. Coverage score is based on documentation completeness rather than source coverage (as calculated by step-03).
68
+
67
69
  ### 2. Read Category Scores from Output
68
70
 
69
71
  Read `{outputFile}` and extract the category scores calculated in previous steps:
@@ -185,6 +185,10 @@ class Installer {
185
185
  await fs.copy(src, path.join(skfDir, file));
186
186
  }
187
187
  }
188
+
189
+ // Write VERSION file for SKF version resolution in installed projects
190
+ const packageJson = require('../../../package.json');
191
+ await fs.writeFile(path.join(skfDir, 'VERSION'), packageJson.version, 'utf8');
188
192
  }
189
193
 
190
194
  /**