bmad-module-skill-forge 1.3.0 → 1.4.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 (40) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/docs/_data/pinned.yaml +1 -1
  3. package/docs/workflows.md +34 -15
  4. package/package.json +2 -2
  5. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +58 -0
  6. package/src/shared/scripts/schemas/skill-brief.v1.json +77 -0
  7. package/src/shared/scripts/schemas/workspace-detection.v1.json +44 -0
  8. package/src/shared/scripts/skf-detect-language.py +277 -0
  9. package/src/shared/scripts/skf-detect-workspaces.py +427 -0
  10. package/src/shared/scripts/skf-emit-brief-result-envelope.py +257 -0
  11. package/src/shared/scripts/skf-extract-public-api.py +29 -0
  12. package/src/shared/scripts/skf-forge-tier-rw.py +73 -0
  13. package/src/shared/scripts/skf-merge-ccc-exclusions.py +14 -0
  14. package/src/shared/scripts/skf-recommend-scope-type.py +369 -0
  15. package/src/shared/scripts/skf-validate-brief-inputs.py +293 -0
  16. package/src/shared/scripts/skf-write-skill-brief.py +509 -0
  17. package/src/skf-audit-skill/steps-c/step-01-init.md +49 -3
  18. package/src/skf-audit-skill/steps-c/step-03-structural-diff.md +6 -5
  19. package/src/skf-brief-skill/SKILL.md +41 -12
  20. package/src/skf-brief-skill/assets/description-voice-examples.md +19 -0
  21. package/src/skf-brief-skill/assets/scope-templates.md +5 -0
  22. package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -40
  23. package/src/skf-brief-skill/references/draft-checkpoint.md +46 -0
  24. package/src/skf-brief-skill/references/headless-args.md +22 -0
  25. package/src/skf-brief-skill/references/headless-source-authority-detection.md +26 -0
  26. package/src/skf-brief-skill/references/portfolio-similarity-check.md +35 -0
  27. package/src/skf-brief-skill/references/qmd-collection-registration.md +52 -0
  28. package/src/skf-brief-skill/references/version-resolution.md +46 -0
  29. package/src/skf-brief-skill/steps-c/step-01-gather-intent.md +164 -14
  30. package/src/skf-brief-skill/steps-c/step-02-analyze-target.md +118 -50
  31. package/src/skf-brief-skill/steps-c/step-03-scope-definition.md +48 -5
  32. package/src/skf-brief-skill/steps-c/step-04-confirm-brief.md +33 -19
  33. package/src/skf-brief-skill/steps-c/step-05-write-brief.md +93 -97
  34. package/src/skf-brief-skill/steps-c/step-06-health-check.md +11 -2
  35. package/src/skf-create-skill/steps-c/step-07-generate-artifacts.md +9 -1
  36. package/src/skf-export-skill/steps-c/step-01-load-skill.md +11 -3
  37. package/src/skf-export-skill/steps-c/step-03-generate-snippet.md +8 -2
  38. package/src/skf-export-skill/steps-c/step-04-update-context.md +29 -0
  39. package/src/skf-export-skill/steps-c/step-06-summary.md +12 -0
  40. package/src/skf-setup/steps-c/step-01b-ccc-index.md +10 -3
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  nextStepFile: './step-04-confirm-brief.md'
3
3
  scopeTemplatesFile: 'assets/scope-templates.md'
4
+ recommendScopeTypeScript: '{project-root}/src/shared/scripts/skf-recommend-scope-type.py'
4
5
  advancedElicitationSkill: '/bmad-advanced-elicitation'
5
6
  partyModeSkill: '/bmad-party-mode'
6
7
  ---
@@ -16,6 +17,8 @@ To collaboratively define the skill's inclusion and exclusion boundaries using t
16
17
  - Focus only on defining scope boundaries — do not write the brief yet (Step 05)
17
18
  - Do not make scope decisions unilaterally — user drives all scope choices
18
19
  - Produce: scope type, include patterns, exclude patterns
20
+ - All user-facing output in `{communication_language}`
21
+ - **Re-entry from step-04 [R] revise:** prior selections (`scope.type`, `scope.include`, `scope.exclude`, `scope.notes`, `scripts_intent`, `assets_intent`, supplemental `doc_urls`) are preserved as the current state. Re-present them at each section as the existing answer; the user only re-confirms or overrides. Do not reset to the §2c template menu unless the user explicitly asks to start scope over.
19
22
 
20
23
  ## MANDATORY SEQUENCE
21
24
 
@@ -65,6 +68,10 @@ Add, remove, or confirm these URLs."
65
68
 
66
69
  Wait for confirmation. Record any changes to `doc_urls`.
67
70
 
71
+ HEAD-check the URLs in parallel — issue all N `curl -sI --max-time 5 {url}` calls in a **single message with N parallel Bash calls**, then process the responses together. On a 4xx/5xx, DNS failure, or timeout per URL, warn `"Could not reach {url} — {status or error}."` and offer the same correct/keep choice as step-01 §3. The check is best-effort — never HALT on a failed HEAD — but the failure must surface here so it is not discovered downstream during compilation.
72
+
73
+ **On re-entry from step-04 [R]:** if `doc_urls` is byte-identical to the list that was probed on the previous pass through this subsection AND the prior per-URL probe results are still recoverable from conversation context, skip the parallel HEAD-check and reuse those results. Re-running the probes when the list has not changed wastes round-trips and can flap on transient failures. Any addition, removal, or edit to a URL invalidates the cache — re-probe the entire updated set. If the prior results are not recoverable (long session, compaction, etc.), re-probe — never cache-hit on a list whose results you cannot cite.
74
+
68
75
  **If no supplemental doc_urls were collected:** Skip this subsection.
69
76
 
70
77
  **Scope guidance for first-time users:** A well-scoped skill covers one cohesive capability with 3-8 primary functions. If the scope includes unrelated concerns (e.g., authentication AND data visualization), suggest splitting into separate briefs. If the scope is too narrow (single utility function), suggest expanding to the surrounding capability surface.
@@ -73,11 +80,41 @@ Wait for confirmation. Record any changes to `doc_urls`.
73
80
 
74
81
  Load `{scopeTemplatesFile}` for the scope type options ([F], [M], [P], [C], [R]) and their descriptions.
75
82
 
76
- Present: "**How broadly should this skill cover the library?**" followed by the scope type options from the loaded reference.
83
+ **Recommend a scope type — don't present the five options as equal weight.** SKILL.md states this workflow "steers toward the smaller, sharper version when scope is unclear" surface that opinion at decision time. Use the analysis from step-02 and the user's intent from step-01 to pick the best-fit recommendation, then present the menu with that option marked as the suggested default.
84
+
85
+ **Delegate the recommendation to `{recommendScopeTypeScript}`** instead of walking the heuristic ladder in prose. The script is the single source of truth for the five-rule ladder (component-registry → reference-app keywords → specific-modules naming/count → narrow-public-api → default full-library) plus the docs-only short-circuit. Both the interactive recommendation and the §6 headless GATE invoke the same script — same inputs, same outputs, no drift.
86
+
87
+ **Fetch registry-file contents before building the payload.** Step-02 §4.1 fetches `package.json` plus the entry-point files but does not fetch `registry.ts` / `components.ts` — the deep-match branch of the component-registry rule needs those contents. Scan the tree for any of `registry.ts` / `registry.tsx` / `components.ts` / `components.tsx` (any depth). For each match, fetch its contents in **one message with N parallel Bash calls** (`gh api repos/{owner}/{repo}/contents/{path}` for GitHub, file reads for local), then base64-decode the responses together. Skip the fetch if the tree contains no registry files.
88
+
89
+ Build the payload and invoke:
90
+
91
+ ```bash
92
+ echo '{
93
+ "intent": "<combined intent + scope_hint text from step-01>",
94
+ "module_count": <count from step-02 §4.3>,
95
+ "export_count": <count from step-02 §4.3>,
96
+ "tree": [<flat list of repo-relative file paths from step-02 §1>],
97
+ "entry_files": [{"path": "<registry path>", "content": "<contents>"}, ...],
98
+ "source_type": "source",
99
+ "mode": "interactive"
100
+ }' | uv run {recommendScopeTypeScript}
101
+ ```
102
+
103
+ `entry_files` carries the registry contents fetched above; omit when no registry files exist in the tree. `mode: "interactive"` activates the content-inspection branch of the component-registry rule (10+ entries or `Component[]` annotation); the headless GATE in §6 uses `mode: "headless"` which falls back to presence-only matching. `source_type: "docs-only"` short-circuits to `docs-only` regardless of the other signals.
104
+
105
+ The script returns `{scope_type, matched_heuristic, signals, rationale}`. Use `rationale` directly — it already names the specific signals that fired.
106
+
107
+ Present:
108
+
109
+ "**Recommended scope type: [{letter}] {Name}** — {rationale from the script}.
110
+
111
+ How broadly should this skill cover the library?
112
+
113
+ {full menu from `{scopeTemplatesFile}` with the recommended letter marked, e.g. '[F] Full Library', '[M] Specific Modules', '[P] Public API Only ← recommended', '[C] Component Library', '[R] Reference App'}
77
114
 
78
- Ask: "Which scope type fits your needs?"
115
+ Press Enter to accept the recommendation, or pick a different letter."
79
116
 
80
- Wait for user selection.
117
+ Wait for user selection. Empty input or just Enter accepts the recommendation; any of the five letters overrides.
81
118
 
82
119
  ### 3. Define Boundaries Based on Selection
83
120
 
@@ -128,19 +165,25 @@ Record the response as `scripts_intent` and `assets_intent` in the brief. Defaul
128
165
 
129
166
  ### 6. Present MENU OPTIONS
130
167
 
131
- Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Continue to Brief Confirmation
168
+ Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Continue to Brief Confirmation [X] Cancel and exit
132
169
 
133
170
  #### Menu Handling Logic:
134
171
 
135
172
  - IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
136
173
  - IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
137
174
  - IF C: Load, read entire file, then execute {nextStepFile}
175
+ - IF X: Treat as user-cancellation. Display `"Cancelled — no brief was written."` and HALT (exit code 6, `halt_reason: "user-cancelled"`). Cancellation here is non-destructive — no files have been written yet. `[X]` is interactive-only; the headless GATE never reaches this branch.
138
176
  - IF Any other comments or queries: help user respond then [Redisplay Menu Options](#6-present-menu-options)
139
177
 
140
178
  #### EXECUTION RULES:
141
179
 
142
180
  - ALWAYS halt and wait for user input after presenting menu
143
- - **GATE [default: C]** — If `{headless_mode}`: accept auto-detected scope (full-repo or manifest-based) and auto-proceed, log: "headless: using auto-detected scope"
181
+ - **GATE [default: C]** — If `{headless_mode}`: consume the headless inputs from step-01 in priority order:
182
+ - If `scope_type` was supplied, use it (must match one of the six valid types) and skip the §2c template menu.
183
+ - Otherwise auto-select via `{recommendScopeTypeScript}` — invoke the script with the **same payload shape** documented in §2c but with `mode: "headless"` (presence-only matching for the component-registry rule, since `entry_files` may not be available without an interactive context). Use the returned `scope_type` and log `"headless: scope_type={value} from heuristic={matched_heuristic}"`. The script's docs-only short-circuit handles `source_type=docs-only` automatically.
184
+ - If `include`/`exclude` were supplied, use them verbatim (split on comma) instead of running the boundary prompts in §3.
185
+ - If `scripts_intent`/`assets_intent` were supplied, record them and skip §5b; otherwise default to `detect`.
186
+ - Log: `"headless: scope_type={value} include={n} exclude={n} scripts_intent={value} assets_intent={value}"`.
144
187
  - ONLY proceed to next step when user selects 'C'
145
188
  - After other menu items execution, return to this menu
146
189
  - User can chat or ask questions — always respond and then redisplay menu
@@ -16,16 +16,15 @@ To present the complete skill brief in human-readable format, highlighting all f
16
16
 
17
17
  - Focus only on presenting and confirming — do not write files yet (Step 05)
18
18
  - Do not proceed without explicit user approval (P2 confirmation gate)
19
+ - All user-facing output in `{communication_language}`
19
20
 
20
21
  ## MANDATORY SEQUENCE
21
22
 
22
23
  **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
23
24
 
24
- ### 1. Load Schema
25
+ ### 1. Assemble Complete Brief
25
26
 
26
- Load `{briefSchemaFile}` to reference required fields and the human-readable presentation format.
27
-
28
- ### 2. Assemble Complete Brief
27
+ Use the values already accepted in steps 01-03 directly — do not re-load `{briefSchemaFile}` here. The 18 fields below are all in conversation; the schema is only consulted in §4 if an inline adjustment needs a specific field's validation rule cited.
29
28
 
30
29
  Compile all gathered data from steps 01-03 into the complete brief:
31
30
 
@@ -48,11 +47,11 @@ Compile all gathered data from steps 01-03 into the complete brief:
48
47
  - **assets_intent:** {detect/none/description from step 03, or "detect" if not explicitly set}
49
48
  - **source_authority:** {official/community/internal from step 01 — default "community"}
50
49
 
51
- ### 3. Present Brief for Review
50
+ ### 2. Present Brief for Review
52
51
 
53
- Using the presentation format from the schema:
52
+ Using the format below:
54
53
 
55
- "**Please review the complete skill brief before I write it.**
54
+ "**Review the complete skill brief before I write it.**
56
55
 
57
56
  ---
58
57
 
@@ -62,7 +61,7 @@ Skill Brief: {name}
62
61
 
63
62
  Target: {source_repo}
64
63
  Language: {language}
65
- Forge Tier: {forge_tier}
64
+ Forge Tier: {forge_tier} — {tier_gloss}
66
65
  Description: {description}
67
66
 
68
67
  Scope: {scope.type}
@@ -79,9 +78,8 @@ Doc URLs:
79
78
  Supplemental Docs:
80
79
  {doc_urls, one per line with labels}
81
80
 
82
- {If scripts_intent or assets_intent was explicitly set (not default "detect"):}
83
- Scripts: {scripts_intent}
84
- Assets: {assets_intent}
81
+ Scripts: {scripts_intent} {scripts_gloss}
82
+ Assets: {assets_intent} — {assets_gloss}
85
83
 
86
84
  Source Authority: {source_authority}
87
85
 
@@ -95,17 +93,31 @@ Created: {created}
95
93
  Created by: {created_by}
96
94
  ```
97
95
 
98
- ---"
96
+ ---
97
+
98
+ Glosses (substitute the matching one-liner for `{tier_gloss}`, `{scripts_gloss}`, `{assets_gloss}` above so the user can decode each value at a glance):
99
+
100
+ - **Forge tier glosses** —
101
+ - `Quick`: text-only extraction; AST and semantic discovery off
102
+ - `Forge`: AST-grep on; semantic and re-ranking off
103
+ - `Forge+`: AST-grep + ccc semantic discovery; re-ranking off
104
+ - `Deep`: full pipeline — AST + ccc + qmd portfolio search + LLM re-ranking
105
+
106
+ - **`scripts_intent` / `assets_intent` glosses** —
107
+ - `detect`: SKF will scan source for the standard `scripts/`/`bin/`/`tools/`/`cli/` (or `assets/`/`templates/`/`schemas/`/`configs/`) directories during create-skill and decide automatically
108
+ - `none`: no script/asset packaging — create-skill will skip the detection pass
109
+ - free-text (anything else): a description of what to package; create-skill treats it as the user's spec
110
+
111
+ (For `docs-only` and `public-api` scope types the scripts/assets prompt is skipped in step-03 §5b — the values default to `detect` but the create-skill detection pass also no-ops for these scope types, so the gloss just clarifies that the recorded value will not actually fire any scan.)"
99
112
 
100
- ### 4. Highlight Items Needing Attention
113
+ ### 3. Highlight Items Needing Attention
101
114
 
102
115
  Flag any fields that may need review:
103
116
 
104
117
  {If language was overridden or low confidence:}
105
118
  "**Note:** Language was {auto-detected / manually overridden}."
106
119
 
107
- {If description was derived (not stated by user):}
108
- "**Note:** Description was derived from your stated intent. Adjust if needed."
120
+ "**Description:** synthesized and confirmed in step-01 §7b. This is the text agents read when deciding whether to route to your skill — refine here if you want to tighten it now that the full brief is visible."
109
121
 
110
122
  {If forge tier was defaulted:}
111
123
  "**Note:** Forge tier defaulted to Quick (no forge-tier.yaml found)."
@@ -123,16 +135,17 @@ You can:
123
135
  - Revise scope boundaries by selecting [R]
124
136
  - Proceed to write by selecting [C]"
125
137
 
126
- ### 5. Handle Inline Adjustments
138
+ ### 4. Handle Inline Adjustments
127
139
 
128
140
  If the user requests changes to specific fields (name, description, version, etc.):
141
+ - If the adjustment requires explaining a field's validation rule or allowed values, load `{briefSchemaFile}` now (otherwise skip the read — the common path does not need it)
129
142
  - Make the adjustment
130
143
  - Re-present the updated brief
131
144
  - Return to the menu
132
145
 
133
- ### 6. Present MENU OPTIONS
146
+ ### 5. Present MENU OPTIONS
134
147
 
135
- Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Party Mode [C] Approve and Write
148
+ Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Party Mode [C] Approve and Write [X] Cancel and exit
136
149
 
137
150
  #### Menu Handling Logic:
138
151
 
@@ -140,7 +153,8 @@ Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Par
140
153
  - IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
141
154
  - IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
142
155
  - IF C: Load, read entire file, then execute {nextStepFile}
143
- - 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)
156
+ - IF X: Treat as user-cancellation. Display `"Cancelled no brief was written."` and HALT (exit code 6, `halt_reason: "user-cancelled"`). Cancellation here is non-destructive — step-05 has not run, no skill-brief.yaml file exists yet. `[X]` is interactive-only; the headless GATE never reaches this branch.
157
+ - IF Any other comments or queries: help user respond, apply any field adjustments, re-present brief if changed, then [Redisplay Menu Options](#5-present-menu-options)
144
158
 
145
159
  #### EXECUTION RULES:
146
160
 
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  briefSchemaFile: 'assets/skill-brief-schema.md'
3
+ versionResolutionFile: 'references/version-resolution.md'
4
+ qmdRegistrationFile: 'references/qmd-collection-registration.md'
3
5
  nextStepFile: './step-06-health-check.md'
6
+ writeSkillBriefScript: '{project-root}/src/shared/scripts/skf-write-skill-brief.py'
7
+ emitBriefEnvelopeScript: '{project-root}/src/shared/scripts/skf-emit-brief-result-envelope.py'
8
+ forgeTierRwScript: '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
9
+ forgeTierFile: '{sidecar_path}/forge-tier.yaml'
4
10
  ---
5
11
 
6
12
  # Step 5: Write Brief
@@ -15,145 +21,136 @@ To generate the complete skill-brief.yaml from the approved brief data and write
15
21
  - Do not change any field values without user request — the brief was already approved
16
22
  - Create the output directory if it doesn't exist
17
23
  - Chains to the local health-check step via `{nextStepFile}` after completion — the user-facing success summary is NOT the terminal step
24
+ - All user-facing output in `{communication_language}`; written artifact (`description`, `notes`) in `{document_output_language}`
25
+ - **Determinism delegation:** YAML rendering, version-precedence, atomic write, the headless result envelope, and the QMD-collection registry mutation are all delegated to shared SKF scripts. The LLM's job in this step is to assemble inputs, branch on script results, and surface user-facing prose — not to render YAML, JSON envelopes, or YAML-mutation diffs in the model.
18
26
 
19
27
  ## MANDATORY SEQUENCE
20
28
 
21
29
  **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
22
30
 
23
- ### 1. Load Schema Template
31
+ ### 1. Reference the Schema (LLM context only)
24
32
 
25
- Load `{briefSchemaFile}` to reference the YAML template structure.
33
+ `{briefSchemaFile}` and `{versionResolutionFile}` document the brief contract for human readers. The deterministic enforcement of that contract lives in `{writeSkillBriefScript}` and its JSON Schema artifact at `src/shared/scripts/schemas/skill-brief.v1.json`. Load `{briefSchemaFile}` only if you need to explain a specific field to the user during inline adjustments — otherwise skip the read; the script is the source of truth.
26
34
 
27
- ### 2. Create Output Directory
35
+ ### 2. Resolve Output Path
28
36
 
29
- Create the directory `{forge_data_folder}/{skill-name}/` if it doesn't already exist.
37
+ Resolve the target write path:
38
+ - Primary: `{forge_data_folder}/{skill-name}/skill-brief.yaml`
39
+ - Fallback (when `{forge_data_folder}` is not set or doesn't exist): `{output_folder}/forge-data/{skill-name}/skill-brief.yaml` and inform user "**Note:** forge_data_folder not configured. Writing to {output_folder}/forge-data/{skill-name}/ instead."
30
40
 
31
- If `{forge_data_folder}` is not set or doesn't exist:
32
- - Fall back to `{output_folder}/forge-data/{skill-name}/`
33
- - Inform user: "**Note:** forge_data_folder not configured. Writing to {output_folder}/forge-data/{skill-name}/ instead."
41
+ The script's atomic-write helper creates parent directories as needed (`mkdir -p`) no separate mkdir call required.
34
42
 
35
- ### 3. Generate skill-brief.yaml
43
+ ### 2b. Existing Brief — Overwrite Policy
36
44
 
37
- **Resolve the `version` field before generating the YAML:**
45
+ Before writing, check whether the resolved target path already exists.
38
46
 
39
- - If `target_version` was set in step 01 (the user explicitly asked for a specific version), use `target_version` as the value of the `version` field. This is the authoritative version for create-skill.
40
- - Otherwise, use the auto-detected source version from step 02, or `1.0.0` if none was detected.
47
+ **Interactive (`{headless_mode}` is false):**
41
48
 
42
- `target_version` and `version` must never carry different values in the written brief. When the user provided a `target_version`, also include it as a separate `target_version` field so downstream tooling can distinguish "user-requested" from "auto-detected" without re-deriving the provenance — but its value must be identical to `version`.
49
+ If the file exists, present:
43
50
 
44
- Generate the YAML file using the approved field values and the schema template:
51
+ "**An existing brief was found at `{path}`.**
52
+ Overwrite it with the brief you just approved? [Y/N]"
45
53
 
46
- ```yaml
47
- ---
48
- name: "{approved skill name}"
49
- version: "{resolved version — target_version if set, else detected source version, else 1.0.0}"
50
- source_type: "{source or docs-only}"
51
- source_repo: "{approved source repo or doc site URL}"
52
- language: "{approved language}"
53
- description: "{approved description}"
54
- forge_tier: "{approved forge tier}"
55
- created: "{current date}"
56
- created_by: "{user_name}"
57
- scope:
58
- type: "{approved scope type}"
59
- include:
60
- - "{approved include patterns}"
61
- exclude:
62
- - "{approved exclude patterns}"
63
- notes: "{approved scope notes or empty string}"
64
- ---
65
- ```
54
+ - **[Y]** Overwrite — proceed to §3.
55
+ - **[N]** Cancel — emit a single-line stderr log `brief-skill: overwrite-cancelled at {path}` and HALT with exit code 5 (do not chain to step-06; the run produced no new artifact).
66
56
 
67
- **Conditional optional field inclusion:**
57
+ **Headless (`{headless_mode}` is true):**
68
58
 
69
- **If `target_version` was set in step 01:**
70
- Include the `target_version` field in the generated YAML — its value MUST be identical to the `version` field above:
71
- ```yaml
72
- target_version: "{target_version — same value as version}"
73
- ```
59
+ If the file exists:
74
60
 
75
- **If `source_type: "docs-only"` OR supplemental `doc_urls` were collected:**
76
- Include the `doc_urls` array (uncommented) in the generated YAML:
77
- ```yaml
78
- doc_urls:
79
- - url: "{documentation URL}"
80
- label: "{page label}"
81
- ```
82
- 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.
61
+ - If `force` was supplied as a headless argument: log `"headless: force-overwriting existing brief at {path}"` and proceed to §3.
62
+ - Otherwise: emit the error envelope per §4b with `halt_reason: "overwrite-cancelled"`, then HALT with exit code 5.
83
63
 
84
- **If `scripts_intent` was collected and is not the default `"detect"`:**
85
- Include the `scripts_intent` field (uncommented):
86
- ```yaml
87
- scripts_intent: "{none or description}"
88
- ```
64
+ If the file does not exist, proceed normally.
65
+
66
+ ### 3. Write the Brief
89
67
 
90
- **If `assets_intent` was collected and is not the default `"detect"`:**
91
- Include the `assets_intent` field (uncommented):
92
- ```yaml
93
- assets_intent: "{none or description}"
68
+ Assemble the brief context as a **flat** JSON object — every approved value is a top-level key, scope is split across four `scope_*` keys instead of nested, and every optional field is passed as `null` when not set rather than conditionally omitted. This eliminates the "decide what to omit" cognitive load that previously made this the most expensive HALT-typo site in the workflow:
69
+
70
+ ```json
71
+ {
72
+ "name": "{approved skill name}",
73
+ "target_version": "{target_version from step 01, or null}",
74
+ "detected_version": "{auto-detected version from step 02, or null}",
75
+ "source_type": "{source or docs-only}",
76
+ "source_repo": "{approved source repo or doc site URL}",
77
+ "language": "{approved language}",
78
+ "description": "{approved description}",
79
+ "forge_tier": "{Quick|Forge|Forge+|Deep}",
80
+ "created": "{current ISO date YYYY-MM-DD}",
81
+ "created_by": "{user_name}",
82
+ "scope_type": "{approved scope type}",
83
+ "scope_include": ["{approved include patterns}"],
84
+ "scope_exclude": ["{approved exclude patterns}"],
85
+ "scope_notes": "{approved scope notes or empty string}",
86
+ "doc_urls": null | [{"url": "...", "label": "..."}],
87
+ "scripts_intent": null | "{detect|none|free-text}",
88
+ "assets_intent": null | "{detect|none|free-text}",
89
+ "source_authority": null | "{official|community|internal}"
90
+ }
94
91
  ```
95
92
 
96
- **Always include** `source_authority` (default: `"community"`, forced to `"community"` when `source_type: "docs-only"`):
97
- ```yaml
98
- source_authority: "{official|community|internal}"
93
+ Pipe it into the writer script with the `--from-flat` flag:
94
+
95
+ ```bash
96
+ echo '<context-json>' | uv run {writeSkillBriefScript} write --target {resolved-target-path} --from-flat
99
97
  ```
100
98
 
101
- ### 4. Write the File
99
+ The script translates flat → nested internally, drops the null optional fields, and runs the same schema validation and atomic write as before — pass every key always, the writer decides what reaches the YAML.
102
100
 
103
- Write the generated YAML to `{forge_data_folder}/{skill-name}/skill-brief.yaml`.
101
+ The script:
102
+ - Validates the context against `src/shared/scripts/schemas/skill-brief.v1.json`
103
+ - Applies the version-precedence rule from `{versionResolutionFile}`
104
+ - Enforces the `target_version == version` invariant (refuses to write a brief that violates it)
105
+ - Renders YAML in canonical key order (byte-stable across runs)
106
+ - Atomically writes the file via temp + fsync + rename (no half-written file ever visible)
107
+ - Emits a JSON success envelope on stdout: `{"status":"ok","brief_path":"…","version":"…","bytes":…,"warnings":[…]}`
104
108
 
105
- If write fails: **HALT** "**Error:** Failed to write skill-brief.yaml. Please check that the directory is writable and try again."
109
+ **On script failure (non-zero exit):**
110
+ - Exit 1 (validation/invariant): The error JSON on stderr names the offending field. This indicates a context-assembly bug, not a user error — surface the message to the user, log it, then HALT.
111
+ - Interactive: **HALT** — display the error JSON's `message` field.
112
+ - Headless: emit the error envelope per §4b with `halt_reason: "input-invalid"`, then `exit 2`.
113
+ - Exit 2 (I/O failure): The atomic write failed (target unwritable, disk full, etc.).
114
+ - Interactive: **HALT** — "**Error:** Failed to write skill-brief.yaml. Check that the directory is writable and try again."
115
+ - Headless: emit the error envelope per §4b with `halt_reason: "write-failed"`, then `exit 4`.
106
116
 
107
- ### 5. QMD Collection Registration (Deep Tier Only)
117
+ **On success:** capture `brief_path` and `version` from the response envelope — both are needed for §4b and §6.
108
118
 
109
- **IF forge tier is Deep AND QMD tool is available:**
119
+ **Draft cleanup.** After a successful write, remove `{forge_data_folder}/{skill-name}/.brief-draft.json` if it exists (`rm -f` — silent on absent). The draft was a step-01 §7 checkpoint covering the in-flight workflow window; once the brief is written it is no longer meaningful. In headless mode this rm is a no-op (drafts are only written interactively).
110
120
 
111
- Index the skill brief into a QMD collection so that portfolio-level searches can find existing briefs and avoid duplicate skill creation across large monorepos.
121
+ ### 4b. Headless Result Envelope (Canonical)
112
122
 
113
- **Collection creation:**
123
+ This section is the canonical envelope-emission reference for the workflow. Every headless emission — the success terminal here and every HARD HALT in step-01/02/05 — uses this contract. Remote sites point here instead of restating it.
124
+
125
+ **Success (this call site only — emitted from §3 directly):**
114
126
 
115
- Create a QMD collection targeting only the brief file:
116
127
  ```bash
117
- qmd collection add {forge_data_folder}/{skill-name} --name {skill-name}-brief --mask "skill-brief.yaml"
118
- qmd embed
128
+ echo '{"status":"success","brief_path":"<from §3 response>","skill_name":"<name>","version":"<from §3 response>","language":"<language>","scope_type":"<scope.type>","halt_reason":null}' | \
129
+ uv run {emitBriefEnvelopeScript} emit
119
130
  ```
120
131
 
121
- If collection already exists (re-briefing): remove and recreate for atomic replace:
132
+ **Error (used by every HARD HALT site):**
133
+
122
134
  ```bash
123
- qmd collection remove {skill-name}-brief
124
- qmd collection add {forge_data_folder}/{skill-name} --name {skill-name}-brief --mask "skill-brief.yaml"
125
- qmd embed
135
+ echo '{"status":"error","skill_name":"<name>","halt_reason":"<reason>"}' | \
136
+ uv run {emitBriefEnvelopeScript} emit --target stderr
126
137
  ```
127
138
 
128
- **Embed verification:**
139
+ When the HALT fires before `skill_name` has been resolved (step-01 §1 pre-flight write probe, step-01 §8 input-missing on a malformed args bundle), pass the partially-gathered value or the literal `"unknown"` — the script accepts any non-empty string at this position.
129
140
 
130
- After `qmd embed` completes, verify the collection was embedded:
131
- - Run `qmd status` or `qmd collection list` and confirm `{skill-name}-brief` shows document count > 0
132
- - If verification succeeds: proceed to registry update normally
133
- - 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
141
+ The script derives `exit_code` deterministically from `halt_reason` (null→0, input-missing/input-invalid→2, forge-tier-missing/target-inaccessible/gh-auth-failed→3, write-failed→4, overwrite-cancelled→5, user-cancelled→6 [interactive-only — headless never raises this]), validates against `src/shared/scripts/schemas/skf-brief-result-envelope.v1.json`, and prints the prefixed `SKF_BRIEF_RESULT_JSON: {…}` line.
134
142
 
135
- **Registry update:**
143
+ The script enforces the success/error halt_reason invariant (success requires null halt_reason; error requires non-null). The `user-cancelled` halt_reason is accepted for completeness (interactive `[X]` Cancel sites in step-01/03/04) but never appears on the headless code path.
136
144
 
137
- Read `{sidecar_path}/forge-tier.yaml` and update the `qmd_collections` array.
145
+ Invocation sites (each pointed at this block, not duplicated): step-01 §1 (write-failed pre-resolution), step-01 §8 (input-missing/input-invalid GATE), step-05 §2b (overwrite-cancelled), step-05 §3 (input-invalid/write-failed from script). Step-01 §1 forge-tier-missing and step-02 §1 (target-inaccessible/gh-auth-failed) currently HALT without emitting an envelope — pre-existing gaps tracked separately, not addressed by this canonicalization.
138
146
 
139
- If an entry with `name: "{skill-name}-brief"` already exists, replace it. Otherwise, append:
147
+ When `{headless_mode}` is false, skip this section silently — no envelope is emitted.
140
148
 
141
- ```yaml
142
- - name: "{skill-name}-brief"
143
- type: "brief"
144
- source_workflow: "brief-skill"
145
- skill_name: "{skill-name}"
146
- created_at: "{current ISO date}"
147
- # status: "pending" # Added only when embed verification fails
148
- ```
149
-
150
- Write the updated forge-tier.yaml.
149
+ ### 5. QMD Collection Registration (Deep Tier Only)
151
150
 
152
- **Error handling:**
153
- - If QMD collection creation fails: log the error. Do NOT fail the workflow — the brief file was already written successfully.
154
- - If forge-tier.yaml update fails: log the error, continue.
151
+ **IF forge tier is Deep AND QMD tool is available:** load `{qmdRegistrationFile}` and follow the procedure there to index the brief into a QMD collection and update the forge-tier registry.
155
152
 
156
- **IF forge tier is NOT Deep:** Skip this section silently. No messaging.
153
+ **IF forge tier is NOT Deep OR QMD is not available:** skip this section silently — do not load `{qmdRegistrationFile}`. No messaging.
157
154
 
158
155
  ### 6. Display Success Summary
159
156
 
@@ -161,7 +158,7 @@ Write the updated forge-tier.yaml.
161
158
 
162
159
  ---
163
160
 
164
- **File:** `{forge_data_folder}/{skill-name}/skill-brief.yaml`
161
+ **File:** `{brief_path from §3 response}`
165
162
  **Skill:** {name}
166
163
  **Language:** {language}
167
164
  **Scope:** {scope type}
@@ -190,4 +187,3 @@ ONLY WHEN the brief file has been written and the success summary displayed will
190
187
  ## CRITICAL STEP COMPLETION NOTE
191
188
 
192
189
  This step chains to the local health-check step (`{nextStepFile}`), which in turn delegates to `shared/health-check.md`. After the health check completes, the brief-skill workflow is fully done.
193
-
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  # `shared/health-check.md` resolves relative to the SKF module root
3
- # (`_bmad/skf/` when installed, `src/` during development), NOT relative
4
- # to this step file.
3
+ # (`{project-root}/_bmad/skf/` when installed, `{project-root}/src/`
4
+ # during development), NOT relative to this step file.
5
5
  nextStepFile: 'shared/health-check.md'
6
6
  ---
7
7
 
@@ -16,7 +16,16 @@ Chain to the shared workflow self-improvement health check at `{nextStepFile}`.
16
16
  - No user-facing reports, file writes, or result contracts in this step — those belong in step-05
17
17
  - Delegate directly to `{nextStepFile}` with no additional commentary
18
18
  - Do not attempt any other action between loading this step and executing `{nextStepFile}`
19
+ - All user-facing output in `{communication_language}`
19
20
 
20
21
  ## MANDATORY SEQUENCE
21
22
 
22
23
  Load `{nextStepFile}`, read it fully, then execute it.
24
+
25
+ ## Completion criteria
26
+
27
+ This is the terminal step of brief-skill. The workflow is complete when `{nextStepFile}` returns control — do not transition to any further step.
28
+
29
+ ## CRITICAL STEP COMPLETION NOTE
30
+
31
+ Step 06 is the terminal stage of brief-skill. After `{nextStepFile}` returns control, the brief-skill workflow is fully complete — do not re-enter step-05 or step-06, do not load any further step file, and do not loop back into the workflow.
@@ -106,11 +106,19 @@ The helper returns non-zero (exit 2) if `{skill_group}/active` already exists as
106
106
  ### 5. Verify Write Completion
107
107
 
108
108
  After all files are written, verify:
109
- - All 4 deliverable artifact types exist (SKILL.md, context-snippet.md, metadata.json, at least one file in references/), all 3 workspace artifacts exist (provenance-map.json, evidence-report.md, extraction-rules.yaml), plus scripts/ and assets/ files when inventories are non-empty
109
+ - All 4 deliverable artifact types exist (SKILL.md, context-snippet.md, metadata.json, **and** either at least one file in `references/` **or** `references/` is empty AND Tier-2 content is inline in SKILL.md — see "Empty `references/` exception" below), all 3 workspace artifacts exist (provenance-map.json, evidence-report.md, extraction-rules.yaml), plus scripts/ and assets/ files when inventories are non-empty
110
110
  - The `active` symlink at `{skill_group}/active` resolves to `{version}`
111
111
  - Store `ref_count` = count of files written to `references/` for use in step-08 report
112
112
  - List each file with its path and size
113
113
 
114
+ **Empty `references/` exception (Tier-2 inline):** `ref_count == 0` is a valid completion state when step-06 kept Tier-2 content inline in SKILL.md — e.g., the body was already under the size limit, or `skill-check` was unavailable and the manual fallback (step-06 §3) skipped the split. In that case, append a single line to `{forge_version}/evidence-report.md` recording the inline state so downstream tooling and audits can distinguish "inline by design" from "split-body skipped due to error":
115
+
116
+ ```
117
+ ref_count: 0 # Tier-2 kept inline in SKILL.md (no split performed in step-06)
118
+ ```
119
+
120
+ When `ref_count > 0` is expected (because step-06 ran a split) but no files were written, halt with: "Split-body produced zero reference files. Investigate step-06 output before retrying — empty `references/` after a split is never a valid state."
121
+
114
122
  **If any write failed:**
115
123
  Halt with: "Artifact generation failed: could not write `{file_path}`. Check permissions and disk space."
116
124
 
@@ -165,14 +165,22 @@ Load `{sidecar_path}/preferences.yaml` (if exists):
165
165
 
166
166
  ### 4b. Check Test Report (Quality Gate)
167
167
 
168
- Search for a test report at `{forge_data_folder}/{skill_name}/{active_version}/test-report-{skill_name}.md` (i.e., `{forge_version}/test-report-{skill_name}.md`). If not found at the versioned path, fall back to `{forge_data_folder}/{skill_name}/test-report-{skill_name}.md`:
168
+ `skf-test-skill` writes timestamped test-report filenames (`test-report-{skill_name}-{ISO-TIMESTAMP}-{HASH}.md`) there is no exact-name `test-report-{skill_name}.md` on disk. Locate the most recent report by glob, not by exact filename:
169
+
170
+ 1. Glob `{forge_data_folder}/{skill_name}/{active_version}/test-report-{skill_name}-*.md` (i.e. `{forge_version}/test-report-{skill_name}-*.md`). Sort matches descending by the parsed ISO-timestamp segment in the filename (`YYYYMMDDTHHMMSSZ` between the skill name and the hash — `sort -r` on the filename works because the timestamp is the first variable component). Take the first match.
171
+ 2. If the versioned glob returns nothing, fall back to the same glob at the flat path `{forge_data_folder}/{skill_name}/test-report-{skill_name}-*.md`. Pick the newest by parsed timestamp.
172
+ 3. If neither glob returns anything, look for the stable companion `skf-test-skill-result-latest.json` in the same two directories (versioned first, then flat). Read the report path from `outputs[]` per the canonical contract documented at `shared/references/output-contract-schema.md` (resolved by skf-test-skill step-06 §4c) and load that file.
173
+ 4. If all three lookups fail, the skill has no test report.
174
+
175
+ **If a test report is found:**
169
176
 
170
- **If test report found:**
171
177
  - Read frontmatter `testResult` and `score`
172
178
  - If `testResult: fail`: warn: "**Warning:** This skill failed its last test (score: {score}%). Consider running `@Ferris TS` and addressing gaps before export."
173
179
  - If `testResult: pass`: note: "Last test: **PASS** ({score}%)"
180
+ - Always surface the actual file picked in the message (e.g. `test-report-my-base-ui-20260507T050917Z-487606-9b2f.md`) — not the no-longer-existent `test-report-{skill_name}.md` — so an operator can navigate to the report from the log.
181
+
182
+ **If no test report found** (all three lookups returned nothing):
174
183
 
175
- **If no test report found:**
176
184
  - Warn: "**Note:** No test report found for this skill. Consider running `@Ferris TS` before export to verify completeness."
177
185
 
178
186
  Continue to step 5 regardless — this is advisory, not blocking.
@@ -62,9 +62,15 @@ Store `{skill_root}` for use in snippet generation. The context-snippet.md writt
62
62
  3. Read SKILL.md to extract: heading slugs for `#quick-start` and `#key-types`, inline summary of key types (~10 words)
63
63
  4. **Anchor verification (split-body awareness):** For each section anchor (`#quick-start`, `#key-types`), verify the heading exists in SKILL.md. If a `references/` directory exists and `## Full` headings in SKILL.md are absent or stubs (indicating split-body, not a stack skill's structural references), rewrite the anchor to point to the reference file path (e.g., `references/{file}.md#key-types`). If the heading cannot be resolved in either location, omit that anchor line from the snippet.
64
64
  5. Derive gotchas from: T2-future annotations in evidence report (breaking changes), async requirements, version-specific behavior.
65
+
66
+ **Detect first-export state before applying carry-forward logic.** The `[CARRIED]` one-cycle expiry is meaningful only on a *re-export*. On a first export, the prior `context-snippet.md` was authored by `create-skill` (or `update-skill`) from the evidence report inside the same forge cycle — those gotchas are freshly derived, not "left over from a previous export." Treating them as carry-forward primes them for premature expiry on the second export.
67
+
68
+ Read `{skills_output_folder}/.export-manifest.json` (the same file step-04 §4a will rewrite). If the skill name is **absent** from `exports`, OR present with no resolvable `last_exported` for any version under `versions`, this is a first export — set `is_first_export = true`. Otherwise `is_first_export = false`. (Step-04 reads the manifest authoritatively for the rebuild; this read is the lightweight probe step-03 needs to choose the right branch below.)
69
+
65
70
  - **If new gotchas are derived:** Use them (they supersede any prior gotchas). Write as `|gotchas: {pitfall-1}, {pitfall-2}` with no marker.
66
- - **If NO new gotchas are derived BUT `prior_gotchas` exists AND `prior_gotchas_already_carried == false`:** First carry-forward cycle preserve the prior gotchas line, prefixing the value with `[CARRIED]` so the next export can detect that expiry has been reached. Write as `|gotchas: [CARRIED] {prior gotchas content}`. Emit warning: "**Gotchas preserved from prior export (one-cycle carry-forward).** These gotchas will be DROPPED on the next export unless new gotchas are derived or you manually refresh them. Review now if they are still applicable."
67
- - **If NO new gotchas are derived AND `prior_gotchas` exists AND `prior_gotchas_already_carried == true`:** Expiry reacheddrop the gotchas line entirely. Emit warning: "**Stale gotchas dropped** the prior gotchas were already carried forward once and cannot be derived from the current evidence report. The snippet now has no gotchas line. If the prior gotchas are still relevant, re-add them to the evidence report's T2-future section and re-run export."
71
+ - **If NO new gotchas are derived AND `is_first_export == true` AND `prior_gotchas` exists:** Treat the prior gotchas as **freshly derived** by create-skill/update-skill write them **without** the `[CARRIED]` marker. (The marker only applies to re-exports.) No warning needed; this is the normal first-export shape.
72
+ - **If NO new gotchas are derived BUT `prior_gotchas` exists AND `is_first_export == false` AND `prior_gotchas_already_carried == false`:** First carry-forward cycle on a re-export preserve the prior gotchas line, prefixing the value with `[CARRIED]` so the next export can detect that expiry has been reached. Write as `|gotchas: [CARRIED] {prior gotchas content}`. Emit warning: "**Gotchas preserved from prior export (one-cycle carry-forward).** These gotchas will be DROPPED on the next export unless new gotchas are derived or you manually refresh them. Review now if they are still applicable."
73
+ - **If NO new gotchas are derived AND `prior_gotchas` exists AND `prior_gotchas_already_carried == true`:** Expiry reached (re-export only — first-export branch above takes precedence) — drop the gotchas line entirely. Emit warning: "**Stale gotchas dropped** — the prior gotchas were already carried forward once and cannot be derived from the current evidence report. The snippet now has no gotchas line. If the prior gotchas are still relevant, re-add them to the evidence report's T2-future section and re-run export."
68
74
  - **If NO new gotchas derived AND no `prior_gotchas`:** Omit the gotchas line.
69
75
 
70
76
  Generate: