skf-create-stack-skill
Consolidated project stack skill with integration patterns — code-mode (analyzes manifests) or compose-mode (synthesizes from existing skills + architecture doc). Use when the user requests to "create a stack skill", "forge a stack", or "stack this project".
Install
npx skills add https://github.com/armelhbobdad/bmad-module-skill-forge/tree/main/src/skf-create-stack-skill
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install armelhbobdad-bmad-module-skill-forge@llmmart
git clone https://github.com/armelhbobdad/bmad-module-skill-forge.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole armelhbobdad/bmad-module-skill-forge collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Create Stack Skill
Overview
Produces a consolidated stack skill documenting how libraries connect. Code-mode analyzes dependency manifests and co-import patterns from actual source code. Compose-mode synthesizes from pre-generated individual skills and architecture documents when no codebase exists yet. Every finding must trace to actual code with file:line citations; in compose-mode, inferred integrations are permitted but must be labeled [inferred from shared domain].
Conventions
- Bare paths (e.g.
references/<name>.md) resolve from the skill root. - The
knowledge/andshared/prefixes are the exception: they resolve from the SKF module root ({project-root}/_bmad/skf/when installed,src/during development), not the skill root — they point at module-shared reference docs (knowledge/tool-resolution.md,knowledge/version-paths.md) and scripts/schemas (shared/references/…) that live once at the module root, mirroring the resolution notereferences/health-check.mdcarries forshared/health-check.md. references/holds prompt content carved out of SKILL.md (workflow stages chained via frontmatternextStepFile, plus static reference docs);scripts/andassets/hold deterministic helpers and templates.{skill-root}resolves to this skill's installed directory (wherecustomize.tomllives, if present).{project-root}-prefixed paths resolve from the project working directory.{skill-name}resolves to the skill directory's basename.
Role
You are a dependency analyst and integration architect. You bring expertise in dependency analysis, cross-library integration patterns, and compositional architecture, while the user brings their project knowledge and scope preferences.
Workflow Rules
These rules apply to every step in this workflow:
- Zero hallucination — all extracted content must trace to actual source code (compose-mode inferences must be labeled)
- Only load one step file at a time — never preload future steps
- If any instruction references a subprocess or tool you lack, achieve the outcome in your main context thread
- Always communicate in
{communication_language} - If
{headless_mode}is true, auto-proceed through confirmation gates with their default action and log each auto-decision - Warnings use a single accumulator — see
## Workflow state contractbelow for shape and surfacing.
Workflow state contract
Every step that emits a warning appends a structured entry to a single in-memory list named workflow_warnings[] (the one accumulator for the whole workflow). Each entry has the shape {step: "step-NN", severity: "info|warn|error", code: "<short-slug>", message: "<human text>", context: {<optional fields>}}. Step 7 surfaces these in evidence-report.md; step 8 may add validation findings; step 9 §5 reads the accumulated list and renders the user-facing "Warnings" section.
Single-pass — no mid-run checkpoint. State lives in memory until step 7 commits provenance-map.json/evidence-report.md; the workflow keeps no resumable checkpoint and On Activation does not probe for a prior run — the analysis is deterministic and cheap to redo, and the two gates are trivially re-confirmed. If interrupted before that commit, restart from step 1.
Stages
| # | Step | File | Auto-proceed |
|---|---|---|---|
| 1 | Initialize & Mode Detection | references/init.md | No (confirm) |
| 2 | Detect Manifests | references/detect-manifests.md | Yes |
| 3 | Rank & Confirm Libraries | references/rank-and-confirm.md | No (confirm) |
| 4 | Parallel Extract | references/parallel-extract.md | Yes |
| 5 | Detect Integrations | references/detect-integrations.md | Yes |
| 6 | Compile Stack | references/compile-stack.md | No (review) |
| 7 | Generate Output | references/generate-output.md | Yes |
| 8 | Validate | references/validate.md | Yes |
| 9 | Report | references/report.md | Yes |
| 10 | Workflow Health Check | references/health-check.md | Yes |
Invocation Contract
| Aspect | Detail | |
|---|---|---|
| Inputs | project_path [required], mode (code/compose) [auto-detected] | |
| Gates | step 3: Confirm Gate [C] | step 6: Review Gate [C] |
| Outputs | SKILL.md (stack), context-snippet.md, metadata.json | |
| Headless | All gates auto-resolve with default action when {headless_mode} is true |
|
| Exit codes | See "Exit Codes" below |
Exit Codes
Every HARD HALT in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
| Code | Meaning | Raised by (halt_reason) |
|---|---|---|
| 0 | success | step 10 (terminal handoff to shared health-check) |
| 2 | input / precondition invalid | step 1 §0 config.yaml missing/malformed (config-missing); step 2 §2 headless with no manifests (no-manifests, S2); step 4 §3 all extractions failed (all-extractions-failed, B7); step 5 §2 feasibility-report schemaVersion mismatch (schema-version-mismatch) |
| 3 | resolution-failure | step 1 §1 forge-tier.yaml missing (forge-tier-missing); step 2 §0 compose-mode skill-resolution corruption (manifest + symlink both fail); step 2 §0 compose-mode zero qualifying skills (S1/B4); step 4 §0 compose-cycle — all resolution-failure |
| 4 | write-failure | step 7 §1 stage-dir / commit-dir failure; step 7 §1 group-dir collision when an existing non-stack skill occupies the target path — both write-failure |
| 6 | user-cancelled | any interactive menu in step 3 / step 6 when the user selects [X] Cancel and exit (user-cancelled) |
Result Contract (Headless)
When {headless_mode} is true, step 9 emits a single-line JSON envelope on stdout before chaining to step 10, and every HARD HALT emits the same envelope shape on stderr with status: "error":
SKF_STACK_RESULT_JSON: {"status":"success|error","skill_package":"…|null","skill_name":"…","stack_libraries":["…"],"mode":"code|compose","exit_code":0,"halt_reason":null}
status is "success" on the terminal happy path, "error" on any HALT. skill_package is the absolute path to the committed stack-skill directory (or null on error before commit). skill_name is the stack skill's published name (e.g. {project_name}-stack). stack_libraries is the array of library names included in the stack (constituent skill names in compose-mode, dependency names in code-mode). mode is "code" or "compose" per the run's resolved mode (null if the run halts before mode resolution). halt_reason is one of: null (success), "config-missing", "forge-tier-missing", "no-manifests", "all-extractions-failed", "schema-version-mismatch", "resolution-failure", "write-failure", "user-cancelled". exit_code matches the table above. Fields unknown at the halt point are null (skill_name) or [] (stack_libraries) — e.g. a config-missing halt precedes project_name resolution.
On Activation
Load config from
{project-root}/_bmad/skf/config.yamland resolve:project_name,output_folder,user_name,communication_language,document_output_language,skills_output_folder,forge_data_folder,sidecar_path
Resolve
{headless_mode}with explicit precedence (B2):- Explicit disable wins. If
--headless=falseor--no-headlesswas passed,{headless_mode}isfalseregardless of any preference. - Explicit enable next. If
--headlessor-Hwas passed (without=false),{headless_mode}istrue. - Preferences fallback. Otherwise, read
headless_modefrom{sidecar_path}/preferences.yaml(trueorfalse). - Default:
false.
- Explicit disable wins. If
Resolve workflow customization. Run:
python3 {project-root}/_bmad/scripts/resolve_customization.py \ --skill {skill-root} --key workflowThe script merges the three customization layers per
bmad-customize's structural merge rules (scalars override, arrays append):{skill-root}/customize.toml— bundled defaults_bmad/custom/<skill-name>.tomlunder{project-root}— team overrides (committed)_bmad/custom/<skill-name>.user.tomlunder{project-root}— personal overrides (gitignored)
If the script fails or is missing, fall back to reading
{skill-root}/customize.tomldirectly — the bundled defaults are an empty string for each path scalar.Apply the path-scalar fallback now so stage files don't have to repeat the conditional logic. For each of the five scalars, if the merged value is empty or absent, use the bundled default:
{stackSkillTemplatePath}←workflow.stack_skill_template_pathif non-empty, elseassets/stack-skill-template.md{integrationPatternsPath}←workflow.integration_patterns_pathif non-empty, elsereferences/integration-patterns.md{manifestPatternsPath}←workflow.manifest_patterns_pathif non-empty, elsereferences/manifest-patterns.md{composeModeRulesPath}←workflow.compose_mode_rules_pathif non-empty, elsereferences/compose-mode-rules.md{provenanceMapSchemaPath}←workflow.provenance_map_schema_pathif non-empty, elseassets/provenance-map-schema.md
Also resolve
{onCompleteCommand}←workflow.on_completeif non-empty, else empty string (no-op —references/report.md§6c skips the hook invocation entirely).Stash all five paths plus
{onCompleteCommand}as workflow-context variables. Stage files reference{stackSkillTemplatePath}/{integrationPatternsPath}/{manifestPatternsPath}/{composeModeRulesPath}/{provenanceMapSchemaPath}directly; empty-string overrides fall through to the bundled default.Also apply the array surfaces: run
workflow.activation_steps_prependnow, keepworkflow.persistent_factsas standing context (file:entries load their contents), then runworkflow.activation_steps_appendafter.Load, read the full file, and then execute
references/init.mdto begin the workflow.
Files (bmad-module-skill-forge)
-
assets
-
provenance-map-schema.md 4.9 KB
--- type: static-reference --- # provenance-map.json Schema Canonical schema templates for the workspace `provenance-map.json` artifact written in step 7 §7. Two variants exist — choose by the run's resolved mode: - **code-mode** — when the workflow analyzed a real codebase with manifest files and AST-extracted exports - **compose-mode** — when the workflow synthesized a stack from pre-generated constituent skills + an architecture document Both variants share the top-level `provenance_version`, `skill_name`, `skill_type`, `generated_at`, `entries[]`, and `integrations[]` shape. They differ in source-anchor fields (`source_repo` / `source_commit` / `source_ref`), in `entries[].extraction_method` values, in `integrations[].detection_method` values, and in compose-mode's additional `constituents[]` array which enables drift detection via metadata-hash comparison. > **Note:** Per-export entries use the same schema as single skills (see `skill-sections.md`), with `source_library` identifying the originating library. In compose-mode, `constituents[]` enables audit to detect constituent drift via metadata hash comparison. ## Code-mode variant Used when the workflow ran in code-mode against an actual codebase. `source_repo` and `source_commit` capture the upstream anchor(s); `entries[].extraction_method` records how each export was discovered (`ast_bridge`, `source_reading`, or `qmd_bridge`). `integrations[].detection_method` is `"co-import grep"` because integration pairs are confirmed by co-import file evidence. ```json { "provenance_version": "2.0", "skill_name": "{project_name}-stack", "skill_type": "stack", "source_repo": ["{repo_url_1}", "{repo_url_2}"], "source_commit": {"{repo_1}": "{hash_1}", "{repo_2}": "{hash_2}"}, "generated_at": "{ISO-8601}", "entries": [ { "export_name": "{name}", "export_type": "{type}", "source_library": "{library-name}", "params": [], "return_type": "{type}", "source_file": "{file}", "source_line": 0, "confidence": "T1|T1-low|T2", "extraction_method": "ast_bridge|source_reading|qmd_bridge", "signature_source": "T1|T2|T3" } ], "integrations": [ { "libraries": ["{libA}", "{libB}"], "pattern_type": "{type}", "detection_method": "co-import grep", "co_import_files": [{"file": "{path}", "line": 0}], "confidence": "T1|T2" } ] } ``` ## Compose-mode variant Used when the workflow ran in compose-mode against pre-generated constituent skills. Source-anchor fields (`source_repo`, `source_commit`, `source_ref`) are `null` because there is no codebase to anchor against — provenance traces back to the constituent skills instead, captured in the `constituents[]` array. Each entry's `extraction_method` is `"compose-from-skill"`; integrations have `detection_method` of `"architecture_co_mention"` (named in the architecture doc), `"constituent_documented_contract"` (a cross-library contract documented in a constituent skill's integration docs but not co-mentioned in the architecture document — e.g. a grep-verified upstream seam cited from a source skill), or `"inferred_from_shared_domain"` (synthesized inference from shared language/domain, no cited contract). `detection_method` records *how* an edge was discovered; it is orthogonal to `confidence`, which is inherited from the constituent skills per the Confidence Tier Inheritance matrix in `{composeModeRulesPath}` (the integration tier is the weaker of the pair — never forced to a fixed band by detection method). ```json { "provenance_version": "2.0", "skill_name": "{project_name}-stack", "skill_type": "stack", "source_repo": null, "source_commit": null, "source_ref": null, "generated_at": "{ISO-8601}", "entries": [ { "export_name": "{name}", "export_type": "{type}", "source_library": "{library-name}", "params": [], "return_type": "{type}", "source_file": "{from constituent skill}", "source_line": 0, "confidence": "T1|T1-low|T2", "extraction_method": "compose-from-skill", "signature_source": "T1|T2|T3" } ], "integrations": [ { "libraries": ["{libA}", "{libB}"], "pattern_type": "{type}", "detection_method": "architecture_co_mention|constituent_documented_contract|inferred_from_shared_domain", "co_import_files": [], "confidence": "T1|T1-low|T2|T3" } ], "constituents": [ { "skill_name": "{constituent-skill-name}", "skill_path": "skills/{skill-dir}/", "version": "{version from constituent metadata.json}", "composed_at": "{ISO-8601}", "metadata_hash": "sha256:{hash of constituent metadata.json}" } ] } ``` **Use the `metadata_hash` value already stored in workflow state during step 2 (S13) — do NOT re-read and re-hash at step 7 time. The stored hash captures the state as it was at manifest-detection time, which is the correct provenance anchor.** -
stack-skill-template.md 6.2 KB
# Stack Skill Template ## SKILL.md Section Structure ```markdown --- name: {project_name}-stack description: > Stack skill for {project_name} — {lib_count} libraries with {integration_count} integration patterns. Use when working with this project's technology stack. --- # {project_name} Stack Skill > {lib_count} libraries | {integration_count} integration patterns | Forge tier: {tier} ## Integration Patterns ### Cross-Cutting Patterns [Patterns that span 3+ libraries — middleware chains, shared config, etc.] ### Library Pair Integrations [For each detected integration pair:] #### {LibraryA} + {LibraryB} **Type:** {pattern_type} **Pattern:** {description} **Key files:** {file_list} **Confidence:** {T1/T1-low/T2} ## Library Reference Index | Library | Imports | Key Exports | Confidence | Reference | |---------|---------|-------------|------------|-----------| | {name} | {count} | {top_exports} | {tier} | [ref](references/{name}.md) | ## Per-Library Summaries ### {library_name} **Role in stack:** {one-line description of what this library does in this project} **Key exports used:** {comma-separated list} **Usage pattern:** {brief pattern description} **Confidence:** {T1/T1-low/T2} ## Conventions [Project-specific conventions for library usage:] - {convention_1} - {convention_2} ``` ## Sizing Guidance for Large Stacks A stack capstone grows monotonically as patterns and libraries are added, so a mature stack eventually pushes SKILL.md past skill-check's `body.max_lines` budget (default **500**) and/or the `description` past 1024 chars. Pre-empt both ceilings at compile time: - **Catalog placement.** The `Library Reference Index` table and `Per-Library Summaries` are the largest, most reference-like sections. For a **large stack** (heuristic: **> 6 libraries OR > 6 integration patterns**, the point at which the inline catalog crowds the 500-line budget), author both into `references/stack-catalog.md` and leave only an inline pointer in SKILL.md (see below). For a **small stack**, keep them inline — inline passive context yields higher task accuracy than on-demand retrieval, and small stacks fit comfortably. Integration Patterns and Conventions stay inline regardless (Tier 1, load-bearing). - **Inline pointer form** (replaces the two sections above when extracted): ```markdown ## Library Catalog {lib_count} libraries indexed in [references/stack-catalog.md](references/stack-catalog.md) — reference-index table + per-library summaries. Load it for a specific library's exports or role. ``` - **Description cap.** Keep `description` ≤ 1024 chars. Do NOT enumerate every library by name; the generic "{lib_count} libraries with {integration_count} integration patterns" form already scales. If a per-library parenthetical is used, cap it to the top libraries by import/export count with a `+{N} more` suffix — the full list lives in `metadata.json` `libraries[]` and the catalog. ## context-snippet.md Format (Vercel-Aligned) Indexed format targeting ~80-120 tokens per stack: ```markdown [{project}-stack v{version}]|root: skills/{project}-stack/ |IMPORTANT: {project}-stack — read SKILL.md before writing integration code. Do NOT rely on training data. |stack: {dep-1}@{v1}, {dep-2}@{v2}, {dep-3}@{v3} |integrations: {pattern-1}, {pattern-2} |gotchas: {1-2 most critical integration pitfalls} ``` ## metadata.json Structure ```json { "skill_type": "stack", "name": "{project}-stack", "version": "1.0.0", "generation_date": "{ISO-8601}", "forge_tier": "{Quick|Forge|Forge+|Deep}", "confidence_tier": "{T1|T1-low|T2|T3}", "spec_version": "1.3", "source_authority": "{official|community|internal}", "generated_by": "create-stack-skill", "exports": [], "library_count": 0, "integration_count": 0, "libraries": ["lib1", "lib2"], "integration_pairs": [["lib1", "lib2"]], "language": "{primary language or list of languages from constituent skills}", "ast_node_count": "{number-or-omitted-if-no-ast}", "confidence_distribution": { "t1": 0, "t1_low": 0, "t2": 0, "t3": 0 }, "tool_versions": { "ast_grep": "{version-or-null}", "qmd": "{version-or-null}", "skf": "{skf_version}" }, "stats": { "exports_documented": 0, "exports_public_api": 0, "exports_internal": 0, "exports_total": 0, "public_api_coverage": 0.0, "total_coverage": 0.0, "scripts_count": 0, "assets_count": 0 }, "dependencies": [], "compatibility": "{semver-range}" } ``` ## references/stack-catalog.md Structure Written **only for large stacks** (see Sizing Guidance) when the catalog is extracted out of SKILL.md. Holds the two sections verbatim from the inline form: ```markdown # {project_name} Stack — Library Catalog > Reference index + per-library summaries extracted from SKILL.md to keep the > capstone under the body-size budget. See SKILL.md for integration patterns. ## Library Reference Index | Library | Imports | Key Exports | Confidence | Reference | |---------|---------|-------------|------------|-----------| | {name} | {count} | {top_exports} | {tier} | [ref](references/{name}.md) | ## Per-Library Summaries ### {library_name} **Role in stack:** {one-line description of what this library does in this project} **Key exports used:** {comma-separated list} **Usage pattern:** {brief pattern description} **Confidence:** {T1/T1-low/T2} ``` ## references/{library}.md Structure ```markdown # {library_name} Reference **Version:** {version_from_manifest} **Import count:** {count} files *(compose-mode: replace with **Export count:** {count} exports)* **Confidence:** {T1/T1-low/T2} ## Key Exports [Top exports used in this project with signatures] ## Usage Patterns [How this library is typically used in this codebase] ## Common Imports [Most frequent import statements] ``` ## references/integrations/{pair}.md Structure ```markdown # {LibraryA} + {LibraryB} Integration **Type:** {pattern_type} **Co-import files:** {count} **Confidence:** {T1/T1-low/T2 [composed]} ## Integration Pattern [Detailed description of how these libraries connect] ## Key Files [Files demonstrating the integration with line references] ## Usage Convention [How this integration is typically structured in the project] ```
-
-
references
-
compile-stack.md 6.4 KB
--- nextStepFile: 'generate-output.md' --- <!-- Config: communicate in {communication_language}. Artifact text in {document_output_language}. --> # Step 6: Compile Stack Skill ## STEP GOAL: Assemble the main SKILL.md by combining per-library extractions with the integration layer, and present for user review before writing output files. ## Rules - Compile SKILL.md following the stack-skill-template structure — integration patterns go first - Do not write output files (Step 07) - Present compiled content for user review ## MANDATORY SEQUENCE ### 1. Load Template Structure Load `{stackSkillTemplatePath}` and prepare SKILL.md section structure. ### 2. Generate Frontmatter The SKILL.md MUST begin with YAML frontmatter (agentskills.io compliance): ```yaml --- name: {project_name}-stack description: > Stack skill for {project_name} — {lib_count} libraries with {integration_count} integration patterns. Use when working with this project's technology stack. NOT for: individual library usage outside this project's conventions. --- ``` **Frontmatter rules:** - `name`: lowercase alphanumeric + hyphens only, must match skill output directory name. **Stack skills MUST end in `-stack`** (e.g., `{project_name}-stack`) — this is how consumers (skf-verify-stack, skf-test-skill) detect stack vs individual skills. - `description`: non-empty, max 1024 chars, trigger-optimized for agent discovery. MUST use third-person voice ("Processes..." not "I can..." or "You can..."). **Do NOT enumerate every library by name** — a 12+ library stack overruns 1024 chars. Keep the generic "{lib_count} libraries with {integration_count} integration patterns" form; if a per-library parenthetical is used, cap it to the top libraries by import/export count with a `+{N} more` suffix (full list lives in `metadata.json` `libraries[]`). See "Sizing Guidance for Large Stacks" in `{stackSkillTemplatePath}`. - No other frontmatter fields — only `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools` are permitted by spec ### 3. Compile Integration Layer Compile in order: **Zero-integration guard:** If the integration graph from step 05 has zero edges (no detected integration pairs), skip the integration layer compilation and note: "No integration patterns detected — stack skill will contain library summaries without an integration layer." Proceed directly to section 4 (Per-Library Sections). **Cross-cutting patterns** (if any): - Patterns spanning 3+ libraries - Middleware chains, shared configuration, common architectural patterns **Library pair integrations:** - For each detected integration pair from step 05: - Type classification - Pattern description with file:line citations - Key files demonstrating the integration - Confidence tier label **Hub library connections:** - For each hub library (3+ connections): - Role in the stack architecture - How it connects to partner libraries ### 4. Compile Per-Library Sections **Catalog placement (decide once, applies to this section and §6).** The `Per-Library Summaries` and `Library Reference Index` are the largest sections and grow with the stack. For a **large stack** (heuristic: **> 6 libraries OR > 6 integration patterns**), author both into `references/stack-catalog.md` (structure in `{stackSkillTemplatePath}`) and place only the inline pointer from the template's "Sizing Guidance" in SKILL.md — this keeps the body under the 500-line `body.max_lines` budget that step 08 enforces. For a **small stack**, keep both inline (inline passive context yields higher task accuracy). Either way, step 08's body-size gate is the backstop; step 08 (`validate.md` §4) accepts both forms. For each confirmed library (ordered by integration connectivity, then import count — **in compose-mode**, order by integration connectivity, then skill confidence tier since import counts are not available): - Role in stack (one-line description) - Key exports used in this project - Usage patterns from extraction - Confidence tier label - Link to reference file: `references/{library}.md` ### 5. Compile Project Conventions Extract project-specific conventions from the extractions: - Common initialization patterns - Error handling approaches across libraries - Configuration conventions - Import organization patterns ### 6. Compile Library Reference Index Place per the §4 catalog-placement decision (inline for small stacks; in `references/stack-catalog.md` for large stacks). Create the reference index table: | Library | Imports | Key Exports | Confidence | Reference | |---------|---------|-------------|------------|-----------| | ... | ... | ... | ... | ... | (**in compose-mode**: replace the Imports column with Export Count from source skill metadata, since import counts are not available) ### 7. Present Compiled SKILL.md Preview "**Stack skill compilation complete. Please review:** --- {Display full compiled SKILL.md content} --- **Compilation stats:** - **Libraries:** {count} - **Integration pairs:** {count} - **Cross-cutting patterns:** {count} - **Confidence:** T1: {count}, T1-low: {count}, T2: {count} **Please review the integration layer and per-library sections.** - Does the integration layer capture how your libraries connect? - Are the per-library summaries accurate? - Any sections to adjust before writing output?" ### 8. Present MENU OPTIONS Display: **Select:** [C] Continue to Output Generation | [X] Cancel and exit #### EXECUTION RULES: - This is a review gate — advancing without the user's `C` would write output files from a compilation they never reviewed. Halt and wait for input after presenting the compiled SKILL.md. - **GATE [default: C]** — If `{headless_mode}`: auto-proceed with [C] Continue, log: "headless: auto-approve stack compilation" - Proceed to the next step only once the user approves by selecting `C`. #### Menu Handling Logic: - IF C: Store skill_content, then load, read entire file, then execute {nextStepFile} - IF X: Invoke the rollback contract (purge any `{forge_data_folder}/{project_name}-stack/{version}/*-tmp` and `*.skf-tmp` staging artifacts under the forge workspace, leave any existing committed stack package untouched), emit the `SKF_STACK_RESULT_JSON` envelope on stderr with `status: "error"`, `halt_reason: "user-cancelled"`, `exit_code: 6`, and exit with code 6 - IF Any other: Process as feedback, adjust compilation, redisplay preview, then [Redisplay Menu Options](#8-present-menu-options) -
compose-mode-rules.md 6.4 KB
<!-- Config: communicate in {communication_language}. --> # Compose Mode Rules Rules for synthesizing a stack skill from pre-generated individual skills and an architecture document, without requiring a codebase. ## Skill Loading The version-aware skill-enumeration protocol (export-manifest primary → active-symlink fallback → stack-skill filter → load metadata fields → store as `raw_dependencies`) is specified operationally in `detect-manifests.md` §0. That step runs at step 2 and has already enumerated and loaded every constituent skill into workflow state before this file is consulted at step 5, so the loading is not re-performed here — this file covers only the compose-mode composition rules below. ## Compose-mode Co-mention Precision Prose co-mention detection is heuristic — it can only provide `Plausible`-class evidence (compared to code-mode's co-imports, which are literal). To reduce false positives the matcher in step 5 §2 applies three guards: 1. **Word-boundary matching** (`\b{skill_name}\b`, case-insensitive). Substring matches are rejected (no `react` inside `reactive`). 2. **Section filtering.** Paragraphs under H1/H2 headers that normalise to `introduction`, `overview`, `glossary`, `table of contents`, `references`, `appendix`, or `index` are excluded — they typically enumerate all libraries without describing integration. Headings themselves are also excluded as co-mention sources. 3. **Two-paragraph minimum.** A pair `(A, B)` requires at least two distinct body paragraphs co-mentioning both names. A single paragraph can be coincidental. **Known limitations:** even with these guards, a co-mention only witnesses that two libraries are discussed together; it does not prove an integration exists. Downstream consumers should prefer stack manifests (`skf-create-stack-skill` output) to prose-derived evidence when both are available. ## Architecture Integration Mapping **If `{architecture_doc_path}` is null or the file does not exist:** Skip this section and proceed to [Inferred Integrations (No Architecture Document)](#inferred-integrations-no-architecture-document) below. 1. Load the architecture document from `{architecture_doc_path}` 2. Parse section headers and prose paragraphs for references to loaded skill names 3. A **co-mention** is detected when a paragraph or section references 2+ loaded skill names 4. For each co-mention pair, load both skills' export lists and API signatures from their `SKILL.md` 5. Compose an integration section describing how the two libraries connect based on: - Shared types or interfaces between the two skills' API surfaces - Architecture document prose describing their interaction - Complementary domain roles (e.g., one produces data the other consumes) ## Confidence Tier Inheritance - All compose-mode evidence inherits confidence tiers from the source individual skills - If both skills in a pair are T1, the integration is T1 - If either skill is T1-low, the integration is T1-low - If either skill is T2, the structural confidence still inherits the lower of T1/T1-low from the pair — the T2 temporal annotations from that skill are carried as an additive enrichment marker, not a tier upgrade - T1 + T2 pair: inherits `T1 [composed, +T2 annotations]` — the T1 skill provides full structural confidence - T1-low + T2 pair: inherits `T1-low [composed, +T2 annotations]` — T1-low structural confidence with T2 temporal annotations noted - If both skills are T2 (no T1/T1-low base available): the integration confidence is `T1-low [composed, +T2 annotations from both]` — T2 temporal enrichment depends on structural extraction, so the most conservative structural tier is assumed - Compose-mode integrations add suffix: `[composed]` — e.g., `T1 [composed]`, `T1-low [composed, +T2 annotations]` ## Integration Evidence Format Each integration entry must cite both source skills by name with function signatures: ``` {Skill A name} + {Skill B name} Type: [pattern type from integration-patterns.md] Evidence: [from skill: {Skill A name}] {exported_function_signature} [from skill: {Skill B name}] {exported_function_signature} Architecture reference: "{quoted prose from architecture doc}" Confidence: {inherited_tier} [composed] ``` ## Feasibility Report Integration The feasibility report contract is defined by the shared schema at `src/shared/references/feasibility-report-schema.md` (single source of truth — `skf-verify-stack` is the producer, this skill is the consumer). Consumers MUST follow the schema verbatim: - **Filename pattern:** `{forge_data_folder}/feasibility-report-{project_slug}-{YYYYMMDD-HHmmss}.md`, with a stable `feasibility-report-{project_slug}-latest.md` copy at the same location. Use `{project_slug}` (slugified `project_name`), not raw `{project_name}`. - **Schema version guard:** Parse frontmatter and confirm `schemaVersion == "1.0"`. On mismatch, HALT with an explicit error; never silently proceed with an unknown version. - **Overall verdict tokens** (frontmatter `overallVerdict`, case-sensitive): exactly one of `FEASIBLE | CONDITIONALLY_FEASIBLE | NOT_FEASIBLE`. - **Per-pair verdict tokens** (in the `## Integration Verdicts` table, case-sensitive): exactly one of `Verified | Plausible | Risky | Blocked`. Any unknown token is a hard error. - Include the verdict in the integration evidence: `VS overall: {overallVerdict}`, `VS pair: {verdict}`. - Flag pairs where VS reported `Risky` or `Blocked`. ## Inferred Integrations (No Architecture Document) When no architecture document is available: - Infer potential integrations from skills sharing the same `language` field - Infer from skills sharing domain keywords in their `SKILL.md` descriptions - Mark all inferred integrations: `[inferred from shared domain]` - Inferred integrations default to lowest confidence of the pair with `[inferred from shared domain]` suffix (use this instead of `[composed]` for inferred integrations) **Constituent-documented contracts (distinct from shared-domain inference):** When a constituent skill's own integration docs cite a verifiable cross-library contract (e.g. a grep-verified upstream seam) that the architecture document does not co-mention, record it with `detection_method: constituent_documented_contract` (see `{provenanceMapSchemaPath}`) — NOT `inferred_from_shared_domain`. It is a cited contract, not a synthesized guess. Its confidence still inherits the weaker tier of the pair per the matrix above — detection method is orthogonal to tier and never forces a fixed band. -
detect-integrations.md 16.8 KB
--- nextStepFile: 'compile-stack.md' pairIntersectProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-pair-intersect.py' - '{project-root}/src/shared/scripts/skf-pair-intersect.py' comentionProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-comention-pairs.py' - '{project-root}/src/shared/scripts/skf-comention-pairs.py' validateFeasibilityReportProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-validate-feasibility-report.py' - '{project-root}/src/shared/scripts/skf-validate-feasibility-report.py' --- <!-- Config: communicate in {communication_language}. --> # Step 5: Detect Integrations ## STEP GOAL: Analyze co-import patterns between confirmed libraries to identify integration points — where and how libraries connect in this specific codebase. ## Rules - Focus on detecting cross-library patterns using subprocess Pattern 1 (grep/search) - Do not compile SKILL.md (Step 06) ## MANDATORY SEQUENCE ### 1. Generate Library Pairs From `confirmed_dependencies`, conceptually you have N*(N-1)/2 unordered pairs. Rather than enumerating and grep-testing each one, prune the matrix via a deterministic **file-list intersection fast path** (MANDATORY first pass, all N): pairs whose per-library file lists do not overlap cannot be integration candidates by construction — drop them. Subsequent grep passes (§2) run only against pairs with a non-empty intersection, and grep scope is restricted to those intersection files rather than the whole source tree. This is NOT a "subprocess-unavailable" fallback; it is the default strategy for every N. Rationale: at N≈21 this collapses 210 prescribed pair greps to ~12 non-empty-intersection pairs in typical codebases; at larger N the compression is even greater. **Compute the intersection deterministically via the shared script:** 1. **Build the libraries JSON** from the per-library file enumeration recorded by step 3 import-count extraction. Skip libraries where step 04 reported extraction failure. Shape: ```json [ {"name": "<library-name>", "files": ["<rel-path-forward-slash>", ...]}, ... ] ``` 2. **Invoke the script** via stdin (or a temp file under `{forge_data_folder}/` if stdin piping is unavailable): **Resolve `{pairIntersectHelper}`** from `{pairIntersectProbeOrder}`; first existing path wins. HALT if no candidate exists. ```bash uv run {pairIntersectHelper} intersect --libraries - ``` piping the libraries JSON on stdin. The script emits: ```json { "pairs": [{"a": "<lib>", "b": "<lib>", "intersection_count": N, "files": [...]}, ...], "truncated": <bool>, "total_pairs": <int> } ``` `pairs[]` is sorted by `intersection_count` DESC, then `(a, b)` ASC for stable ordering. Default Top-K cap is **20** (matches S7 below); pass `--top-k N` to override. 3. **Parse the JSON result** and use `pairs[]` as the qualifying-pair set for §2 onward. The `intersection_count` is the candidate file count for the §2 2-file threshold pre-grep; `files[]` is the grep-scope for that pair. **Top-K cap (S7, 20):** If the script's response has `truncated: true`, more pairs than the cap had non-empty intersections. Surface a user-visible warning composed as: `"non-empty-intersection pair count {total_pairs} exceeds cap — analyzing top 20 by intersection size; {total_pairs - 20} pairs skipped"`. Record this cap in the evidence report. (Tie-break by intersection size is what the script sorts on; if you need to override the cap intentionally, pass `--top-k N` and document the rationale.) The cap is a second-order safety limit, not a primary strategy — the file-intersection prune does the bulk of the work. Report: "**Analyzing {pair_count} library pairs for integration patterns** (pruned from {N*(N-1)/2} via file-list intersection; {capped_count} after Top-K if applicable)**...**" ### 2. Detect Co-Import Files **If `compose_mode` is true:** Instead of co-import grep, detect integrations from architecture document: 1. Load `{composeModeRulesPath}` for integration evidence format rules 2. **If `{architecture_doc_path}` is null or not available:** Skip directly to the "If no architecture document available" fallback below 3. **Compute qualifying co-mention pairs deterministically via the shared script.** The precision guards (word-boundary matching `\b{skill_name}\b` case-insensitive to reject substrings like `react` inside `reactive`; H1/H2 section exclusion for headers that normalise to `introduction`, `overview`, `glossary`, `table of contents`, `references`, `appendix`, or `index`, with heading text itself never a co-mention source; and the ≥2-distinct-body-paragraph gate) are a scan-and-count with one correct answer per `(architecture_doc, skill-name-set)` — do not perform them in-prompt. Risks and rationale stay documented in `{composeModeRulesPath}` under "Compose-mode co-mention precision". **Resolve `{comentionHelper}`** from `{comentionProbeOrder}`; first existing path wins. ```bash uv run {comentionHelper} comention --doc {architecture_doc_path} --skills - ``` piping the loaded skill names — the `confirmed_dependencies` names — as a JSON array on stdin (e.g. `["react", "express"]`; use a temp file under `{forge_data_folder}/` if stdin piping is unavailable). The script emits: ```json { "pairs": [ {"a": "<skill>", "b": "<skill>", "paragraph_count": N, "evidence": [{"header": "<governing-header-or-null>", "excerpt": "<text>"}, ...]}, ... ], "excluded_section_count": M } ``` `pairs[]` contains only qualifying pairs (≥2 distinct body paragraphs), sorted by `paragraph_count` DESC then `(a, b)` ASC. Use `pairs[]` as the detected-integration-pair set for step 4 onward; each pair's `evidence[]` supplies the governing header and paragraph excerpts to quote as architecture-reference evidence. **Graceful degradation:** if no `{comentionProbeOrder}` candidate exists (e.g. `uv` unavailable on claude.ai web), perform the equivalent scan directly per the guards above and the script's `--help` contract, then proceed with the same qualifying-pair set. 4. For each detected integration pair (from `pairs[]`): - Load both skills' export lists and API signatures - Compose an integration section following the format from `{composeModeRulesPath}` - Include VS feasibility verdict if a feasibility report matching the filename pattern defined in `src/shared/references/feasibility-report-schema.md` exists under `{forge_data_folder}/` (timestamped `feasibility-report-{project_slug}-{YYYYMMDD-HHmmss}.md` or the stable `feasibility-report-{project_slug}-latest.md` copy). Schema version `"1.0"` is required; see the schema for the full contract. - Cite evidence from both skills: `[from skill: {skill_name}]` All integration evidence inherits confidence tiers from the source skills. Load and apply the full **Confidence Tier Inheritance** matrix from `{composeModeRulesPath}` to compute the correct tier for each pair (covers T1+T1, T1+T1-low, T1-low+T1-low, T1+T2, T1-low+T2, T2+T2 cases). Apply the `[composed]` suffix to all confidence labels — e.g., `T1 [composed]`, `T1-low [composed, +T2 annotations]`. **VS verdict parsing (if feasibility report exists):** The feasibility report format is defined by the shared schema at `src/shared/references/feasibility-report-schema.md` (single source of truth; skf-verify-stack is the producer, this skill is the consumer). Follow the schema strictly: - Locate the report via the filename pattern in the schema: `{forge_data_folder}/feasibility-report-{project_slug}-{YYYYMMDD-HHmmss}.md` (or the stable `feasibility-report-{project_slug}-latest.md` copy next to it). - **Schema version guard (deterministic gate):** Resolve `{validateFeasibilityReportHelper}` from `{validateFeasibilityReportProbeOrder}` (first existing path wins) and run it against the located report: ```bash python3 {validateFeasibilityReportHelper} <located-report-path> ``` Consume `schemaVersionOk` / `schemaVersionFound` from its JSON. If `schemaVersionOk` is false, HALT with `"feasibility-report schemaVersion mismatch: expected '1.0', got '{schemaVersionFound}' — refusing to proceed"`, then emit the result envelope on stderr per the Result Contract in SKILL.md and exit `2`. The script's structural findings (`headingsOk` / `orderViolations`) are advisory for this consumer — it gates only on schemaVersion. **If the helper does not resolve or cannot run,** parse the report's YAML frontmatter directly and compare `schemaVersion` to the literal `1.0`, applying the same HALT (a missing or mismatched version halts identically). Unknown versions are never interpreted. ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":[],"mode":"compose","exit_code":2,"halt_reason":"schema-version-mismatch"} ``` - Read `overallVerdict` from frontmatter (exactly one of `FEASIBLE|CONDITIONALLY_FEASIBLE|NOT_FEASIBLE`). - Parse the `## Integration Verdicts` markdown table for per-pair verdicts (exactly one of `Verified|Plausible|Risky|Blocked`). Any unknown verdict token is a hard error per the schema — do not silently drop or map. - For each architecture-detected pair, include `VS overall: {overallVerdict}` and `VS pair: {verdict}` in the integration evidence per the format in `{composeModeRulesPath}`. VS verdicts do not apply to inferred integrations since the VS report operates on architecture-described interactions only. Additionally, flag any pairs where VS reported `Risky` or `Blocked` by appending a `[VS: Risky]` or `[VS: Blocked]` warning annotation to the integration entry. If no architecture document available: - Infer potential integrations from skills sharing the same `language` field or sharing domain keywords in their SKILL.md descriptions (use the `usage_patterns` and `exports` fields from `per_library_extractions[]` built in step 4, or reload SKILL.md from the version-aware path: use `skill_package_path` from step 2, or resolve via `{skills_output_folder}/{skill_dir}/active/{skill_dir}/SKILL.md` — see `knowledge/version-paths.md`) - Mark inferred integrations: `[inferred from shared domain]` — use this suffix instead of `[composed]` for inferred integrations - Inferred integrations qualify automatically — no file-count threshold applies Skip to section 3 (Classify Integration Types) with the compose-mode pairs. **If not compose_mode:** For each library pair (A, B): **Launch a subprocess** that greps across all source files to find files importing BOTH library A and library B. Return only file paths and import line numbers. **Subprocess resolution:** Use the Grep tool (Claude Code), built-in search (Cursor), or `grep`/`rg` (CLI). See `knowledge/tool-resolution.md`. **Subprocess returns:** `{pair: [A, B], co_import_files: [{path, line_A, line_B}], count: N}` **Note on file-list intersection:** per §1, the intersection has already been computed and the pair's `intersection_files` set is known. The grep below runs against that set only, not the whole source tree. If a subprocess is entirely unavailable, the intersection itself is the integration evidence — count the intersection files against the 2-file threshold below without the grep. The intersection is mandatory first-pass work, not a fallback. **Threshold:** A pair must have 2+ co-import files to qualify as an integration pattern (single file co-imports may be incidental). **CCC Semantic Augmentation (Forge+ and Deep with ccc):** If `tools.ccc` is true AND `ccc_index.status` is `"fresh"` or `"stale"` in forge-tier.yaml, augment co-import detection with semantic search (max 1 query per library pair): For each library pair with **0 or 1 co-import files** (below the 2-file threshold — S9, symmetric to give the 0-hit case the same chance as the 1-hit case), run `ccc_bridge.search("{libA} {libB}", source_root, top_k=10)` to find files where the two libraries interact semantically — even without explicit import co-location. If CCC returns additional files where both libraries appear, add them to the pair's co-import candidate list and re-evaluate against the 2-file threshold. **CCC precision guard for 1-file pairs (H3):** When a CCC hit would elevate a 1-file pair to qualifying status, run a post-hoc verification on that file: re-grep the file and confirm it contains explicit import statements for **both** libraries (per the ecosystem import patterns from `{manifestPatternsPath}`). If either import is missing (e.g., one library is only name-dropped in a comment or string), drop the CCC-added file from the candidate list. Only pairs with ≥2 files that each contain explicit imports for both libraries qualify. Log rejected CCC candidates in workflow state for the evidence report. **Tool resolution for ccc_bridge.search:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `cd {source_root} && ccc search --limit 10 "{libA} {libB}"` (CLI). `ccc search` reads the index in the current working directory and has no project-selector flag (`--path` is a file-path glob filter *within* the index, and the result cap is `--limit`, not `--top`). See `knowledge/tool-resolution.md`. For pairs that already qualify (2+ files), CCC is not needed for detection — but the CCC results may surface additional integration files for richer classification in section 3. CCC failures: skip augmentation silently, proceed with grep-only results. ### 3. Classify Integration Types Load `{integrationPatternsPath}` for classification rules. For each qualifying pair, analyze to classify the integration type against those pattern types (**in compose-mode**: all architecture-document-detected pairs qualify automatically — the 2+ co-import file threshold applies only in code-mode; **in code-mode**: pair must have 2+ co-import files): For each detected integration: - Identify the top 3 files demonstrating the pattern - Extract a brief description of how the libraries connect - **Assign confidence (M1/M3) — derive from per-library tiers + detection-method qualifier (NOT from AST):** integration detection here is grep + co-import (optionally CCC-augmented), never AST. The integration's confidence is the **weaker** of the two libraries' tiers from `per_library_extractions[]` (tie-break: T1-low > T1, T2 > T1-low, T3 > T2 — never overstate). Then append a detection-method qualifier: - `grep-co-import` — the pair qualified via direct co-import grep (the default). - `ccc-augmented` — the pair qualified only after CCC semantic search elevated it (per §2 CCC augmentation), and the post-hoc import verification (H3) confirmed both imports. - `architecture-co-mention` — compose-mode pair qualified via word-boundary co-mention in the architecture document (per §2 H2 guards). Maps to `detection_method: architecture_co_mention` in `provenance-map.json`. - `constituent-documented-contract` — compose-mode pair whose cross-library contract is documented in a constituent skill's integration docs (cited, e.g. a grep-verified upstream seam) but not co-mentioned in the architecture document. Maps to `detection_method: constituent_documented_contract` in `provenance-map.json`. - `inferred-shared-domain` — compose-mode pair without an architecture document, inferred from shared `language` or domain keywords (no cited contract). Maps to `detection_method: inferred_from_shared_domain` in `provenance-map.json`. - Render as `{tier} ({qualifier})` — e.g., `T1-low (grep-co-import)`, `T1 (ccc-augmented)`, `T1-low (architecture-co-mention) [composed]`. The `[composed]`/`[inferred from shared domain]` suffix from `{composeModeRulesPath}` is appended after the qualifier in compose-mode. **Provenance ↔ SKILL.md tier parity:** The tier derived above is the single value for this edge — write the *same* tier to both the SKILL.md integration label and `provenance-map.json` `integrations[].confidence`. The detection-method qualifier (and the `provenance-map.json` `detection_method` it maps to) records *how* the edge was found and is orthogonal to confidence; it never forces the tier into a fixed band. ### 4. Build Integration Graph Assemble the integration graph: - **Nodes:** Confirmed libraries (with extraction data from step 04) - **Edges:** Detected integration pairs with type, file count, and description - Identify **hub libraries** (connected to 3+ other libraries) - Identify **cross-cutting patterns** (patterns spanning 3+ libraries) ### 5. Display Integration Summary If integrations were detected, report the integration graph (`{lib_count}` libraries, `{pair_count}` pairs): the hub libraries (connected to 3+ others) with their partners, each detected pair (library A, library B, type, co-import file count, confidence tier), and any cross-cutting patterns spanning 3+ libraries. If none were detected, report that no co-import integration patterns were found — the libraries appear to operate independently, so the stack skill will carry library summaries without an integration layer. ### 6. Auto-Proceed to Next Step Load, read the full file and then execute `{nextStepFile}`. -
detect-manifests.md 10.3 KB
--- nextStepFile: 'rank-and-confirm.md' scanManifestsProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-scan-manifests.py' - '{project-root}/src/shared/scripts/skf-scan-manifests.py' enumerateStackSkillsProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-enumerate-stack-skills.py' - '{project-root}/src/shared/scripts/skf-enumerate-stack-skills.py' --- <!-- Config: communicate in {communication_language}. --> # Step 2: Detect Manifests ## STEP GOAL: Scan the project root for dependency manifest files, parse each to extract dependency names and versions, and produce a raw dependency list for ranking. ## Rules - Focus only on finding and parsing manifest files - Do not count imports or rank dependencies (Step 03) or extract documentation (Step 04) - If explicit dependency list was provided in step 01, use it and skip detection ## MANDATORY SEQUENCE ### 0. Check Compose Mode **If `compose_mode` is true AND `explicit_deps` was provided in step 01:** Use the explicit dependency list directly. Store the explicit list as `raw_dependencies` with `source: "explicit"` and skip to [Auto-Proceed to Next Step](#4-auto-proceed-to-next-step). **If `compose_mode` is true AND `explicit_deps` was NOT provided:** Discover skills in `{skills_output_folder}` using version-aware resolution — see `knowledge/version-paths.md` for path templates. **Version-aware skill enumeration:** 1. **Primary: Export manifest** — Read `{skills_output_folder}/.export-manifest.json`. For each entry in `exports`, resolve the active version path: `{skills_output_folder}/{skill-name}/{active_version}/{skill-name}/` — this directory must contain both `SKILL.md` and `metadata.json`. **Stale manifest fallback (H6):** If a manifest entry resolves to a path that does not exist (broken `active_version`, deleted version dir, missing `SKILL.md` / `metadata.json`), do NOT HALT for that single entry. Instead: a. Fall back to the symlink scan (rule 2) **for that one skill only**: probe `{skills_output_folder}/{skill-name}/active/{skill-name}/SKILL.md`. b. If the symlink-based path resolves, use it and log a warning: `"export-manifest entry '{skill-name}' is stale — resolved via active symlink instead"`. c. If BOTH the manifest path AND the symlink path fail, only then HALT with a manifest-corruption diagnostic naming the affected skill and pointing the user at `[SKF-update-skill]` to repair. Emit the result envelope on stderr per the Result Contract in SKILL.md, then STOP: ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":[],"mode":"compose","exit_code":3,"halt_reason":"resolution-failure"} ``` **Manifest JSON parse guard (B3):** Wrap the `.export-manifest.json` parse in try/except. If JSON parsing fails for any reason, fall through entirely to the `active` symlink scan (rule 2) across all skills; log a warning and validate each symlink target exists before including it. 2. **Fallback: `active` symlinks** — If the manifest does not exist, is empty, JSON-parse fails, or an individual manifest entry fails to resolve, scan for `{skills_output_folder}/*/active/*/SKILL.md`. Each match resolves to a skill package at `{skills_output_folder}/{skill-name}/active/{skill-name}/` (the `{active_skill}` template). Verify the active symlink target actually exists and contains both `SKILL.md` and `metadata.json`. **Filter & cycle guard (B4):** Skip any skill where the filter below matches: - Skill name equals `{project_name}-stack`, OR - `metadata.json` has `"skill_type": "stack"`, OR - `metadata.json` is missing or unreadable (treat `skill_type: unknown` as non-loadable — exclude to avoid loading a partially-written or self-referential skill). Maintain a **visited set keyed by `skill_dir`** (the top-level dir under `{skills_output_folder}`) while resolving. If a skill would be revisited via a circular reference (e.g., a constituent that claims another stack as dependency), skip the duplicate and log a warning `"cycle detected at {skill_dir} — skipping"`. Stack skills must not be loaded as source dependencies to avoid self-referencing loops. **If zero skills remain after filtering:** HALT with: "**Cannot proceed in compose-mode.** No individual skills found in `{skills_output_folder}` (after filtering stack skills). Run [CS] Create Skill or [QS] Quick Skill to generate individual skills first, then re-run [SS]." Then emit the result envelope on stderr per the Result Contract in SKILL.md, and STOP: ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":[],"mode":"compose","exit_code":3,"halt_reason":"resolution-failure"} ``` **Deterministic metadata hashing (S13) — script-driven:** Do NOT compute `sha256` in-prompt (a model cannot reproduce a digest, so a hand-computed hash would false-diverge against step 4's script-computed hash). Invoke the same enumeration helper step 4 §0 uses to obtain every constituent's `metadata_hash` in one deterministic call: **Resolve `{enumerateStackSkillsHelper}`** from `{enumerateStackSkillsProbeOrder}`; first existing path wins. HALT if no candidate exists. ```bash uv run {enumerateStackSkillsHelper} enumerate {skills_output_folder} ``` Key the emitted `skills[].metadata_hash` (a `sha256:`-prefixed digest of the raw `metadata.json`, or `null` when no metadata.json is present) by `skills[].name` for use in rule 5 below. The script's `name` is the top-level subdirectory under `{skills_output_folder}` — i.e. the `skill_dir` captured in rule 3, not the metadata `name` — so join on `skill_dir`. For each skill found: 1. Read `metadata.json` from the resolved version-aware path (`{skill_package}` or `{active_skill}`). **Skill-type gate (S1):** the sibling `metadata.json` MUST be present AND parseable AND contain a `skill_type` field whose value is one of the known set (`skill`, `stack`, or any future values explicitly recognised by this workflow). Directories lacking a qualifying `metadata.json`/`skill_type` are NOT treated as skills — log `"{dir_name}: not a skill (no valid metadata.json/skill_type) — excluding"` and skip. 2. Extract: name, language, confidence_tier, source_repo, exports count, version 3. Store the skill group directory name as `skill_dir` (the top-level name under `{skills_output_folder}`, distinct from `name` — the directory may differ from the metadata name) 4. Store the resolved package path as `skill_package_path` for use in later steps 5. **Record the constituent metadata_hash (S13):** take this skill's `metadata_hash` from the enumerate-script inventory above (matched on `skill_dir`) and store it in workflow state alongside `skill_package_path`. The script is the single source of this hash — never hand-compute — so the step-4 drift check compares script-hash to script-hash and never false-positives on a model recomputation. Step-07 uses this stored hash (not a re-read) for `constituents[].metadata_hash` in `provenance-map.json`, so drift between step 2 read and step 7 write is captured. 6. Store as `raw_dependencies` with source: "existing_skill" Report the `{N}` loaded skills — for each: name, language, confidence tier, export count, and source. Skip to [Auto-Proceed to Next Step](#4-auto-proceed-to-next-step) — this loaded-skills summary serves as the detection summary. **If not compose_mode:** Continue with section 1 (existing flow). ### 1. Check for Explicit Dependency List **If `explicit_deps` was provided in step 01:** "**Using provided dependency list.** Skipping manifest auto-detection. **Dependencies:** {explicit_deps_count} libraries provided" Store the explicit list as `raw_dependencies` and skip to [Display Detection Summary](#3-display-detection-summary). **If no explicit list:** Continue to section 2. ### 2. Scan and Parse Manifests Invoke the deterministic manifest scanner — it walks the project root, parses every recognised manifest, dedupes the production dep set, and flags monorepo layout: **Resolve `{scanManifestsHelper}`** from `{scanManifestsProbeOrder}`; first existing path wins. HALT if no candidate exists. ```bash uv run {scanManifestsHelper} scan {scan_root} ``` Where `{scan_root}` is the project root path. Load `{manifestPatternsPath}` for the ecosystem reference table that documents supported filenames, dependency keys, and normalisation rules; the script implements exactly that table (npm/pnpm/yarn, python pip/poetry/pdm, rust cargo, go modules, java/kotlin maven + gradle, ruby bundler, composer, swift package manager). Exclusion patterns (`node_modules/`, `.venv/`, `vendor/`, `dist/`, `build/`, `target/`, `.git/`, hidden dirs) are applied internally. Parse the JSON output — shape: ``` { "manifests": [ {"path": "<rel-from-root>", "ecosystem": "<name>", "deps": [{"name": "...", "version": "..."}]}, ... ], "total_unique": N, "monorepo": <bool>, "warnings": ["..."] // optional, only if any parse warning fired } ``` If `manifests` is empty: **Headless auto-cancel (S2):** If `{headless_mode}` is true, do NOT wait for user input. Emit the result envelope on stderr per the Result Contract in SKILL.md and exit `2`. Headless mode cannot proceed without an explicit dependency list. ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":[],"mode":"code","exit_code":2,"halt_reason":"no-manifests"} ``` **Interactive mode:** "**No dependency manifests detected** in the project root. Searched for: package.json, requirements.txt, Cargo.toml, go.mod, pom.xml, build.gradle, Gemfile, composer.json, *.csproj **Options:** 1. Provide an explicit dependency list 2. Specify a different project root path 3. Cancel workflow **Halting — please provide input.**" STOP — wait for user response. Otherwise, store the parsed `manifests[]` and `total_unique` as `raw_dependencies` (dedup is already applied by the scanner), surface any `warnings[]` to the user as parse-quality notes, and inspect the `monorepo` flag: if `true`, mention the monorepo layout in the detection summary so the user can decide whether to scope the ranking to a specific package or proceed across all manifests. ### 3. Display Detection Summary Report the detected manifests (for each: path, ecosystem, dependency count) and the total unique dependency count split into runtime vs dev-only. ### 4. Auto-Proceed to Next Step Load, read the full file and then execute `{nextStepFile}`. -
generate-output.md 17.4 KB
--- nextStepFile: 'validate.md' # Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order # (installed SKF module path first, src/ dev-checkout fallback); first existing # path wins. HALT if neither resolves — stage/commit/flip-link/write below # MUST go through the atomic helper, per §1 rollback contract. atomicWriteProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py' - '{project-root}/src/shared/scripts/skf-atomic-write.py' # Resolve `{frontmatterValidator}` by probing `{frontmatterValidatorProbeOrder}` # in order (installed SKF module path first, src/ dev-checkout fallback); first # existing path wins. Used by the §8 pre-commit frontmatter + body-size gate # (`--max-body-lines`). If neither resolves, the gate degrades to a WARNING — # step 8 (validate.md) still runs the full post-commit check. frontmatterValidatorProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-validate-frontmatter.py' - '{project-root}/src/shared/scripts/skf-validate-frontmatter.py' --- <!-- Config: communicate in {communication_language}. Artifact text in {document_output_language}. --> # Step 7: Generate Output Files ## STEP GOAL: Write all deliverable and workspace artifact files to their target directories. ## Rules - Write all output files in correct directory structure — do not modify compiled content from Step 06 - Create directory structure before writing files - Report each file written with path and size ## MANDATORY SEQUENCE ### 1. Resolve Paths and Stage Target Directory Resolve `{version}` per S11 below — the primary library version in code-mode, or the stack-local scheme in compose-mode (`1.0.0` for a new stack, or a bump of `{prior_stack_version}` on re-composition). The final artifact paths are: ``` {skill_group} # {skills_output_folder}/{project_name}-stack/ {skill_package} # {skills_output_folder}/{project_name}-stack/{version}/{project_name}-stack/ ├── references/ │ └── integrations/ {forge_version} # {forge_data_folder}/{project_name}-stack/{version}/ ``` Where the skill name is `{project_name}-stack` and `{version}` is the semver version (with build metadata stripped per `knowledge/version-paths.md`). **Primary library definition (S11):** In code-mode, the primary library is the dependency with the highest import count from step 3; its `version` (from the manifest) becomes `{primary_library_version}`, falling back to `1.0.0` if unavailable. In compose-mode, the stack carries its own release identity: default `{version}` to `1.0.0` (a stack-local scheme) rather than borrowing the highest constituent semver. Constituent versions are preserved in `dependencies[]`, so no information is lost, and the stack's version does not track whichever constituent happens to have the highest version. The `1.0.0` default applies only to a **genuinely new** stack — see the re-composition rule below. **Re-composition versioning (S11, compose-mode):** When the S3 pre-flight resolves a `{prior_stack_version}` (re-composing a stack that already has a release line), continue that line instead of resetting — defaulting to `1.0.0` would publish a version *below* the existing release (e.g. `1.0.0` shadowing a prior `3.0.5`, a backward jump). Bump `{prior_stack_version}`: - **Major** if any library in `{prior_libraries}` is removed or replaced — dropping a documented library is breaking for consumers of the stack. - **Minor** otherwise — libraries only added, and/or integration content changed (backward-compatible). Never emit a `{version}` ≤ `{prior_stack_version}`. Narrate the resolved version and the bump rationale. **Pre-flight: group-dir type check (S3):** If `{skills_output_folder}/{project_name}-stack/` already exists, probe `{skills_output_folder}/{project_name}-stack/active/{project_name}-stack/metadata.json`. If that metadata exists and `skill_type != "stack"`, HALT with: "**Cannot proceed.** `{skills_output_folder}/{project_name}-stack/` exists but is not a stack skill (`skill_type={found_type}`). Rename the existing directory or choose a different `project_name` to avoid collision." Do NOT proceed to staging or commit. Emit the result envelope on stderr per the Result Contract in SKILL.md and exit `4` (`stack_libraries` carries the confirmed library names; nothing was committed, so `skill_package` is `null`): ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":["<confirmed-lib>", "..."],"mode":"{code|compose}","exit_code":4,"halt_reason":"write-failure"} ``` If that metadata exists and `skill_type == "stack"`, this run is a **re-composition**: capture its `version` as `{prior_stack_version}` and its `libraries` array as `{prior_libraries}` for the S11 re-composition rule above. If the group dir is absent — or exists but has no resolvable `active` stack metadata — treat this as a new stack and leave `{prior_stack_version}` unset. **Atomic write strategy (C2 / B5):** All artifact writes for `{skill_package}` MUST stage into a temp directory first, then commit atomically via `skf-atomic-write.py commit-dir`. The active symlink flip only happens AFTER the commit succeeds. Create the staging directory: ```bash python3 {atomicWriteHelper} stage-dir --target {skill_package} ``` After this call, writes land in `{skill_package}.skf-tmp/` (referred to below as `{skill_staging}`). Create the required subdirectories inside the staging dir: ```bash mkdir -p {skill_staging}/references/integrations ``` Also create the forge workspace directory directly (these are workspace artifacts, not deliverables — they do not need stage-dir / commit-dir): ```bash mkdir -p {forge_version} ``` **Rollback contract:** If ANY write in sections 2–7 below fails, immediately run: ```bash python3 {atomicWriteHelper} commit-dir --rollback --target {skill_package} ``` Then abort (see B7): purge any `{forge_version}/*-tmp` staging artifacts, emit the result envelope on stderr per the Result Contract in SKILL.md, and halt the workflow. This is the single rollback exit shared by §7 (workspace-write failure) and §9 (commit-dir failure): ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":["<confirmed-lib>", "..."],"mode":"{code|compose}","exit_code":4,"halt_reason":"write-failure"} ``` ### 2. Stage SKILL.md Write the approved `skill_content` from step 06 to `{skill_staging}/SKILL.md` (regular write — the whole staging dir will be atomically committed later). ### 3. Stage Per-Library Reference Files For each confirmed library, write `{skill_staging}/references/{library_name}.md`: Load structure from `{stackSkillTemplatePath}` references section: - Library name, version from manifest (**in compose-mode**: version from source skill `metadata.json`) - Import count and file count (**in compose-mode**: export count from source skill metadata) - Key exports with signatures - Usage patterns with file:line citations (**in compose-mode**: usage patterns from source skill SKILL.md) - Confidence tier label **If the catalog was extracted** (large stack — step 06 §4 placed the `Library Reference Index` + `Per-Library Summaries` out of SKILL.md), also write `{skill_staging}/references/stack-catalog.md` using the structure in `{stackSkillTemplatePath}`, and confirm SKILL.md carries the inline pointer instead of the two sections. Small stacks keep the catalog inline and write no `stack-catalog.md`. ### 4. Stage Integration Pair Reference Files For each detected integration pair, write `{skill_staging}/references/integrations/{libraryA}-{libraryB}.md`: Load structure from `{stackSkillTemplatePath}` integrations section: - Library pair and integration type - Co-import file count - Integration pattern description with file:line citations - Usage convention - Confidence tier label **If no integrations detected:** Skip this section (no files to write). ### 5. Stage context-snippet.md Write `{skill_staging}/context-snippet.md`: Use the Vercel-aligned indexed format targeting **~80-120 tokens** (M2). Token estimation is heuristic — use `ceil(char_count / 4)` as the working approximation (the standard rule-of-thumb for English text in BPE-style tokenizers; precise counts differ per model). Compute against the rendered snippet body (excluding trailing newline). ``` [{project_name}-stack v{version — resolved in §1}]|root: skills/{project_name}-stack/ |IMPORTANT: {project_name}-stack — read SKILL.md before writing integration code. Do NOT rely on training data. |stack: {dep-1}@{v1}, {dep-2}@{v2}, {dep-3}@{v3} |integrations: {pattern-1}, {pattern-2} |gotchas: {1-2 most critical integration pitfalls} ``` **Overflow strategy (M2):** If the estimated token count exceeds **120 tokens**, trim in this fixed order until under budget: 1. **Drop the `gotchas` line first.** Pitfalls live in SKILL.md and references; the snippet's job is discovery, not full warning surface. 2. **Strip versions from the `stack` line** (`{dep-1}, {dep-2}` instead of `{dep-1}@{v1}`). Versions are recoverable from `metadata.json`. 3. **Truncate the `stack` list to the top 8 dependencies by import count** (or by export count in compose-mode), appending `, ...+{N} more`. 4. **Truncate the `integrations` list to the top 5 by file count**, appending `, ...+{N} more`. If the snippet is still over budget after step 4, log a warning to workflow_warnings (see Workflow Rules in SKILL.md) — do not block the write. The `IMPORTANT:` line is mandatory and never trimmed. **Underflow note:** Snippets below ~80 tokens are acceptable (small stacks naturally produce short snippets). The lower bound is informational, not enforced. ### 6. Stage metadata.json Write `{skill_staging}/metadata.json`, populating every field from the metadata.json schema in `{stackSkillTemplatePath}` (loaded in §3) — that template is the single schema source; do not re-transcribe it here. Resolve the field values whose template placeholders don't spell out the rule: - `version` — the `{version}` resolved in §1 (not the template's literal `1.0.0`, which is only the new-stack default). - `forge_tier` — the run tier (Quick/Forge/Forge+/Deep) resolved in step 1. - `confidence_tier` — the dominant T-code from `confidence_distribution`. Pick the tier with the highest count; resolve ties toward the weaker tier (T1-low > T1, T2 > T1-low, T3 > T2) so the reported value never overstates confidence. When `confidence_distribution` is empty (no libraries extracted), emit `"T1-low"` as the conservative default. - `source_authority` — the lowest authority among constituent skills (official > community > internal). ### 7. Write Forge Data Artifacts (Workspace) Write workspace artifacts directly to `{forge_version}` (these are workspace-only, not part of the skill package — no staging required). Each individual file MUST be written via `skf-atomic-write.py write` to avoid partial-write corruption: ```bash <json-content> | python3 {atomicWriteHelper} write --target {forge_version}/provenance-map.json <md-content> | python3 {atomicWriteHelper} write --target {forge_version}/evidence-report.md ``` If any workspace write fails, invoke the rollback contract from §1. **provenance-map.json:** Use the schema from `{provenanceMapSchemaPath}` — see that asset for the canonical templates and field definitions of both variants: - **In code-mode:** use the code-mode variant (`source_repo` / `source_commit` populated; `extraction_method` ∈ `ast_bridge|source_reading|qmd_bridge`; `detection_method = "co-import grep"`). - **In compose-mode:** use the compose-mode variant (source-anchor fields `null`; `extraction_method = "compose-from-skill"`; `detection_method ∈ "architecture_co_mention|constituent_documented_contract|inferred_from_shared_domain"`; includes the additional `constituents[]` array for drift detection). Populate compose-mode `constituents[].metadata_hash` from the value stored in workflow state at step 2 (S13), not a fresh re-hash at step-7 time — `{provenanceMapSchemaPath}` carries the rationale for why the manifest-detection-time hash is the correct provenance anchor. **evidence-report.md:** - Extraction summary per library - Integration detection results per pair - Warnings and failures encountered - Confidence tier distribution ### 8. Pre-Commit Frontmatter & Body-Size Gate The full schema/frontmatter validation runs in step 8 (`validate.md`) — but that runs *after* commit-dir and flip-link have already published the package. The most common non-auto-fixable `skill-check` hard rejects — a `description` over the 1024-char limit (which `skill-check --fix` cannot trim for you) and a SKILL.md body over the `body.max_lines` limit (default **500**) — would therefore only surface on an already-committed, symlink-active artifact, forcing edits to the live `SKILL.md`. `skill-check` also *warns* (non-blocking) when the body exceeds `body.max_tokens` (default **5000**). Additive re-composition of an already-large stack grows the body monotonically, so body overflow is the likeliest trigger. Catch these here, while the package is still in `.skf-tmp`. Resolve `{frontmatterValidator}` from `{frontmatterValidatorProbeOrder}` (first existing path wins). Run it against the **staged** `SKILL.md`, passing the real skill name so the directory-match check is not fooled by the `.skf-tmp` staging suffix, `--max-body-lines 500` to assert the skill-check body-line limit, and `--max-body-tokens 5000` to surface the skill-check body-token *warning* pre-commit (advisory — see disposition below): ```bash uv run {frontmatterValidator} {skill_staging}/SKILL.md --skill-dir-name {project_name}-stack --max-body-lines 500 --max-body-tokens 5000 ``` The validator emits JSON: `status` (`pass`/`warn`/`fail`), `issues[]` (each with `severity` ∈ `high|medium|low`, `field`, `message`), `body_lines` (the counted body size), `body_tokens` (the estimated token count), and `summary`. Disposition: - **`status` is `fail`, OR any `issues[]` entry has `severity` `high` or `medium`** — a hard violation that `npx skill-check` (step 8) would reject and `--fix` cannot auto-correct. HALT-to-fix **in staging**, then re-run the validator until it clears. Remediate by `field`: - `description` / `name` / `compatibility` — trim/correct `{skill_staging}/SKILL.md` (e.g. shorten `description` to ≤ 1024 chars). - `body` (`body lines N exceeds max 500`) — reduce the staged body: prefer a **selective split** of the largest Tier-2 section(s) into `{skill_staging}/references/`, keeping Tier-1 content inline (mirrors `validate.md` §3); or trim redundant content. Re-run the gate until `body_lines ≤ 500`. (An over-`body_tokens` estimate is advisory, not a hard stop — see the low-severity note below.) Do NOT proceed to §9 commit-dir with an unresolved high/medium issue. Note: an over-long `description` is rated `medium` and exits `0`, so key the HALT on the issue severities above — not on the exit code. - **Only `low`-severity issues (e.g. an unexpected field, or a `body token estimate N exceeds max 5000` advisory)** — record each as a WARNING in the evidence report and proceed; these do not block the commit. The body-token estimate is a char/4 heuristic that runs higher than `skill-check`'s own whitespace-split count, and `skill-check` treats `body.max_tokens` as a non-blocking warning — so an over-token estimate is advisory here, not a HALT (the `body.max_lines` gate above remains the hard body pre-check). **If `{frontmatterValidator}` does not resolve** (neither probe path exists) **or the invocation cannot run**, emit a WARNING ("pre-commit frontmatter + body-size gate skipped — validator unavailable") and proceed. Step 8 (`validate.md`) remains the post-commit backstop (including the `body.max_lines` split path in its §3); this gate is a best-effort early catch, never a new hard dependency. ### 9. Commit Staging Directory After all staged writes in sections 2–6 completed successfully, atomically swap the staging dir into place: ```bash python3 {atomicWriteHelper} commit-dir --target {skill_package} ``` The helper moves any existing `{skill_package}` aside to a `.skf-rollback-<pid>` dir before the swap. On failure the helper restores the prior target and exits non-zero — in that case invoke the rollback contract from §1 and HALT. ### 10. Flip Active Symlink ONLY AFTER `commit-dir` succeeds, flip the `{skill_group}/active` symlink to point at `{version}`: ```bash python3 {atomicWriteHelper} flip-link --link {skill_group}/active --target {version} ``` The helper holds an flock on `{skill_group}/active.skf-lock` and refuses to replace a non-symlink at `{skill_group}/active` — this guards against accidentally overwriting a real directory (ECH BLOCKER 6/B6). After the flip, `{skill_group}/active/{project_name}-stack/` resolves to the just-committed skill package. If `flip-link` fails, emit a warning (the committed package is still valid), note the symlink-flip failure in the evidence report, and continue. ### 11. Display Write Summary Report the files written: the `{skill_package}` deliverables (SKILL.md with `{line_count}` lines, context-snippet.md with `{token_estimate}` tokens, metadata.json, `references/` `{lib_count}` library files, `references/integrations/` `{pair_count}` integration files), the `{forge_version}` workspace (provenance-map.json, evidence-report.md), the `{skill_group}/active -> {version}` symlink, and the `{total_count}` total. ### 12. Auto-Proceed to Next Step Load, read the full file and then execute `{nextStepFile}`. -
health-check.md 884 B
--- # `shared/health-check.md` resolves relative to the SKF module root # (`{project-root}/_bmad/skf/` when installed, `src/` during development), NOT relative # to this step file. nextStepFile: 'shared/health-check.md' --- <!-- Config: communicate in {communication_language}. --> # Step 10: Workflow Health Check ## STEP GOAL: Chain to the shared workflow self-improvement health check at `{nextStepFile}`. This is the terminal step of create-stack-skill — after the shared health check completes, the workflow is fully done. ## Rules - No user-facing reports, file writes, or result contracts in this step — those belong in step 9 - Delegate directly to `{nextStepFile}` with no additional commentary - Do not attempt any other action between loading this step and executing `{nextStepFile}` ## MANDATORY SEQUENCE Load `{nextStepFile}`, read it fully, then execute it. -
init.md 6.2 KB
--- nextStepFile: 'detect-manifests.md' forgeTierFile: '{sidecar_path}/forge-tier.yaml' --- <!-- Config: communicate in {communication_language}. --> # Step 1: Initialize ## STEP GOAL: Load forge tier configuration, validate prerequisites, and prepare the stack skill workflow for execution. ## Rules - Focus only on loading configuration and validating prerequisites — do not start analyzing dependencies ## MANDATORY SEQUENCE ### 0. Validate Project Config Before anything else, load `{project-root}/_bmad/skf/config.yaml`. If the file is missing OR fails YAML parse OR lacks the required top-level keys (`project_name`, `output_folder`, `skills_output_folder`, `forge_data_folder`, `sidecar_path`), HALT with: "**Cannot proceed.** SKF is not initialized for this project (config.yaml missing or malformed). **Required:** Run `skf init` first. **Halting workflow.**" Then emit the result envelope on stderr per the Result Contract in SKILL.md (`project_name` is unresolved here, so `skill_name` is `null`), and STOP — do not proceed: ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":null,"stack_libraries":[],"mode":null,"exit_code":2,"halt_reason":"config-missing"} ``` ### 1. Load Forge Tier Configuration Load `{forgeTierFile}` from the Ferris sidecar. **If forge-tier.yaml does not exist:** "**Cannot proceed.** The setup workflow has not been run for this project. **Required:** Run `setup` first to detect available tools and determine your forge tier. **Halting workflow.**" Then emit the result envelope on stderr per the Result Contract in SKILL.md (config loaded, so `skill_name` is known; `mode` is not yet resolved), and STOP — do not proceed: ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":[],"mode":null,"exit_code":3,"halt_reason":"forge-tier-missing"} ``` **If forge-tier.yaml exists:** Extract: - `forge_tier` — Quick, Forge, Forge+, or Deep - `available_tools` — list of detected tools (gh_bridge, ast_bridge, qmd_bridge, skill-check) - `project_root` — project root path **Apply tier override:** Read `{sidecar_path}/preferences.yaml`. If `tier_override` is set and is a valid tier value (Quick, Forge, Forge+, or Deep), use it instead of the detected tier. ### 2. Validate Available Tools **Required for all tiers:** - File I/O capability (read project files) **Tier-dependent tools:** - **Quick:** gh_bridge (source reading) — graceful degradation to local file reading if unavailable - **Forge:** ast_bridge (ast-grep structural analysis) — required for Forge tier - **Forge+:** ast_bridge + ccc_bridge (ccc semantic co-import augmentation) — ccc available for step 5 - **Deep:** qmd_bridge (QMD temporal enrichment) — required for Deep tier See `knowledge/tool-resolution.md` for how each bridge name resolves to concrete tools per IDE environment. Report tool availability. If a tier-required tool is missing, downgrade tier and note: "**Tier adjusted:** {original_tier} → {adjusted_tier} — {missing_tool} unavailable." ### 3. Accept Optional Inputs Check if the user provided: **Explicit dependency list:** - If provided, store as `explicit_deps` and skip auto-detection in step 02 - Format: comma-separated library names or a file path **Scope overrides:** - If provided, store as `scope_overrides` for use in step 03 - Format: `library_name: include|exclude` **Compose mode detection:** Set `compose_mode: false` as the default. Skills use version-nested directories — see `knowledge/version-paths.md` for the full path templates and resolution rules. - If user provides an architecture document path for composition or explicitly requests compose mode → set `compose_mode: true` and store `architecture_doc_path` - If no manifest files exist in project root AND at least one skill is discoverable in `{skills_output_folder}` → suggest compose mode to the user and ask for optional architecture document path - **Skill discovery (version-aware):** First, read `{skills_output_folder}/.export-manifest.json` — each entry in `exports` names a skill with an `active_version`, which resolves to `{skills_output_folder}/{skill-name}/{active_version}/{skill-name}/` containing `SKILL.md` and `metadata.json`. If the export manifest does not exist or is empty, fall back to scanning for `active` symlinks: check `{skills_output_folder}/*/active/*/SKILL.md` — each match indicates a skill whose package lives at `{skills_output_folder}/{skill-name}/active/{skill-name}/` (the `{active_skill}` template). - **Headless default (B8):** If `{headless_mode}` is true, do NOT prompt — auto-accept the suggestion: set `compose_mode: true` and `architecture_doc_path: null` (unless an architecture doc path was supplied via the optional inputs above). This is the constructive default: with no manifests present, code-mode would only halt at step 2 (`no-manifests`), so compose is the sole path that produces output. Log the auto-decision by appending to `workflow_warnings[]`: `{step: "step-01", severity: "info", code: "headless-compose-autodetect", message: "no manifests + {N} discoverable skills — auto-selected compose mode", context: {discoverable_skills: {N}}}`. - If user accepts → set `compose_mode: true` and store `architecture_doc_path` (may be `null` if user chose not to provide one) - If user declines → `compose_mode` remains `false`, continue with code-mode If compose_mode: - Display: "**Compose mode detected.** Synthesizing stack skill from existing skills + architecture document." If no optional inputs provided, auto-detection will be used. ### 4. Display Initialization Summary Report that the Stack Skill Forge is initialized, naming: the project (`{project_name}`); the forge tier (`{forge_tier}`) with its positive-capability framing (Quick = source reading and import counting; Forge = AST-backed structural analysis; Forge+ = AST structural + CCC semantic co-import augmentation; Deep = full intelligence — structural + contextual + temporal); the available tools (`{tool_list}`); and the resolved input mode (auto-detect, explicit dependency list, or compose mode). ### 5. Auto-Proceed to Next Step Load, read the full file and then execute `{nextStepFile}`. -
integration-patterns.md 2.6 KB
<!-- Config: communicate in {communication_language}. --> # Integration Pattern Detection Rules ## Co-Import Detection A co-import is detected when two or more confirmed libraries are imported in the same source file. ### Detection Method 1. For each file in the codebase, extract all import statements 2. Map imports to confirmed library list 3. If a file imports 2+ confirmed libraries → co-import detected 4. Record: file path, library pair, line numbers ### Minimum Threshold - A library pair must co-appear in **2+ files** to qualify as an integration pattern - Single file co-imports may be incidental ## Integration Pattern Types ### Type 1: Middleware Chain Libraries connected in a processing pipeline. - **Signal:** Sequential function calls passing output of one library to another - **Example:** `express` + `cors` — cors middleware registered on express app ### Type 2: Shared Types Libraries exchanging type definitions or data structures. - **Signal:** Type imports from one library used as parameters/returns in another - **Example:** `react` + `react-router` — Router components accepting React elements ### Type 3: Configuration Bridge One library configuring or initializing another. - **Signal:** Config objects or initialization calls referencing both libraries - **Example:** `next` + `tailwindcss` — Tailwind configured via next.config ### Type 4: Event Handler Libraries connected through event emission/handling patterns. - **Signal:** Event listeners from one library triggering actions in another - **Example:** `socket.io` + `redis` — Redis pub/sub driving socket events ### Type 5: Adapter/Wrapper One library wrapping another to provide a unified interface. - **Signal:** Thin wrapper functions delegating to underlying library - **Example:** `prisma` + `zod` — Zod schemas validating Prisma model inputs ### Type 6: State Sharing Libraries sharing application state or context. - **Signal:** Shared state stores, context providers, or global singletons - **Example:** `react` + `zustand` — Zustand stores consumed in React components ## Output Format For each detected integration: ``` Library A + Library B Type: [pattern type] Files: [count] files with co-imports Key files: [top 3 files by integration density] Pattern: [brief description of how they integrate] Confidence: [weaker of the two libraries' tiers from per_library_extractions[], with detection-method qualifier in parens — e.g., `T1-low (grep-co-import)`, `T1 (ccc-augmented)`, `T1-low (architecture-co-mention) [composed]`. Integration detection is grep + co-import, never AST — do not label integrations "AST-verified".] ``` -
manifest-patterns.md 2.5 KB
<!-- Config: communicate in {communication_language}. --> # Manifest Detection Patterns ## Supported Ecosystems | Ecosystem | Manifest File(s) | Dependency Key | Import Pattern | |-----------------------|-----------------------------------------------------|------------------------------------------|-------------------------------------------| | JavaScript/TypeScript | package.json | dependencies, devDependencies | `import ... from '...'`, `require('...')` | | Python | requirements.txt, setup.py, pyproject.toml, Pipfile | install_requires, [project.dependencies] | `import ...`, `from ... import` | | Rust | Cargo.toml | [dependencies] | `use ...`, `extern crate` | | Go | go.mod | require | `import "..."` | | Java | pom.xml, build.gradle | dependencies | `import ...` | | Ruby | Gemfile | gem | `require '...'`, `require_relative` | | PHP | composer.json | require, require-dev | `use ...`, `require_once` | | .NET | *.csproj | PackageReference | `using ...` | <!-- Manifest scanning, name normalization, dedup, exclusion-dir filtering, and dev/build-tool filtering are performed by `skf-scan-manifests.py` (invoked in detect-manifests.md §2), which implements exactly the ecosystem table above. This file is loaded only for that reference table and the import-counting exclusions below — see the script's `--help` for the operative scan contract. --> ## Import Counting For each dependency, count distinct files that import it: - Use grep patterns from Import Pattern column - Count unique file paths, not total import statements - Exclude test files (`*/test/*`, `*_test.*`, `*.spec.*`, `*.test.*`), config files (`*.config.*`, `.eslintrc`, etc.), and build artifacts (`dist/`, `build/`, `node_modules/`, `target/`, `__pycache__/`) from count -
parallel-extract.md 10.8 KB
--- nextStepFile: 'detect-integrations.md' enumerateStackSkillsProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-enumerate-stack-skills.py' - '{project-root}/src/shared/scripts/skf-enumerate-stack-skills.py' --- <!-- Config: communicate in {communication_language}. --> # Step 4: Parallel Library Extraction ## STEP GOAL: For each confirmed dependency, extract key exports, usage patterns, and API surface documentation using tier-dependent tools in parallel. ## Rules - Extract per-library using subprocess Pattern 4 (parallel) when available; if unavailable, extract sequentially - Each subprocess returns structured extraction, not raw file contents - Do not analyze cross-library integrations (Step 05) ## MANDATORY SEQUENCE ### 0. Check Compose Mode **If `compose_mode` is true:** "**Extraction data already available from individual skills. Skipping extraction phase.**" For each confirmed skill, load SKILL.md from the version-aware path resolved in step 2. **Re-resolve at step 4 entry (S17):** Between step 2 manifest detection and step 4 extraction, a concurrent write could have advanced a skill's `active_version`. On entering this step, re-resolve `skill_package_path` for each confirmed skill via the export-manifest (or symlink fallback) and capture the freshly-read `version` and `metadata_hash` into workflow state. If the `metadata_hash` diverges from the value stored in step 2, log a warning `"constituent '{skill_name}' changed between step 2 and step 4 — using fresh values"` and replace the workflow-state entry (so step 7 provenance records the hash active at extraction time, with the drift logged for audit). Use `skill_package_path` (stored in step 2 and optionally refreshed above) directly — this already points to the resolved `{skill_package}` or `{active_skill}` directory containing the skill's artifacts. If `skill_package_path` is not available, resolve via the `{active_skill}` template: `{skills_output_folder}/{skill_dir}/active/{skill_dir}/SKILL.md` (see `knowledge/version-paths.md`). **Exports resolution order (H1) — script-driven:** Do NOT walk per-skill `metadata.json` → `references/` → SKILL.md by hand. Invoke the helper once at step entry to compute the full inventory for every confirmed skill in one deterministic call: **Resolve `{enumerateStackSkillsHelper}`** from `{enumerateStackSkillsProbeOrder}`; first existing path wins. HALT if no candidate exists. ```bash uv run {enumerateStackSkillsHelper} enumerate {skills_output_folder} ``` The script emits JSON of the form: ```json { "skills": [ { "name": "<skill-name>", "path": "<rel-to-skills-root, forward-slash>", "exports": ["..."], "exports_source": "metadata|references|skill-md|unknown", "confidence": "T1|T2|T1-low", "metadata_hash": "sha256:..." | null } ], "cycles": ["<skill-name>"], "warnings": ["<text>"] } ``` Cache this result as `stack_skill_inventory` in workflow state — the per-skill subagent fan-out at §1+ MUST read from this cache rather than re-reading each skill's `SKILL.md` / `metadata.json` / `references/` to determine exports. Append every entry in `warnings[]` to workflow state for the evidence report (the script already labels them per-skill, e.g. `"<skill-name>: no exports found via any resolution path"`). If `cycles[]` is non-empty, a composes-cycle makes the stack unbuildable — emit the result envelope on stderr per the Result Contract in SKILL.md and exit `3`: ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":[],"mode":"compose","exit_code":3,"halt_reason":"resolution-failure"} ``` Build a `per_library_extractions[]` entry for each skill by reading from the cached inventory: - `library`: `inventory.skills[i].name` - `exports`: `inventory.skills[i].exports` - `exports_source`: `inventory.skills[i].exports_source` (one of `metadata|references|skill-md|unknown` — capture for step 7 provenance) - `confidence`: `inventory.skills[i].confidence` (one of `T1|T2|T1-low`; `unknown` source ⇒ `T1-low`). Do not silently drop T3 evidence; carry whatever tier the source skill declared if §1+ subagent analysis upgrades the value. - `metadata_hash`: `inventory.skills[i].metadata_hash` — record for step 7 provenance (null when exports came from references/ or SKILL.md prose). - `usage_patterns`: populated by the §1+ per-skill subagent fan-out, NOT by this script. The script provides the inventory + exports; the subagent does the per-skill usage analysis. They're complementary. Report the loaded extractions — for each skill: export count, confidence tier, and load status. Then auto-proceed to the next step. **If not compose_mode:** Continue with section 1 (existing flow). ### 1. Prepare Extraction Plan **AST Tool Availability Check (Forge/Deep only):** This workflow operates on local project files and installed library packages. Remote source resolution does not apply — libraries are analyzed as they exist within the project's dependency tree. **If AST tool is unavailable at Forge/Deep tier:** ⚠️ **Warn the user explicitly:** "AST tools are unavailable — extraction will use source reading (T1-low). Run [SF] Setup Forge to detect and configure AST tools for T1 confidence." Degrade to Quick tier extraction. Note the degradation reason in context for the evidence report. **Per-file AST failure handling:** If ast-grep fails on an individual file (parse error, unsupported syntax), fall back to source reading for that file only. Label the affected file's results T1-low; unaffected files retain T1. Log a warning noting which file degraded and why. For each library in `confirmed_dependencies`, determine extraction strategy based on forge tier: **Quick Tier:** - Read source files that import the library - Extract usage patterns from import statements and function calls - Identify key exports used in this project - Confidence: T1-low (source reading inference) **Forge Tier (adds to Quick):** - Use ast_bridge to analyze structural exports from library source - Extract function signatures, type definitions, class hierarchies - Map parameter types and return types - Confidence: T1 (AST-verified structural extraction) **Deep Tier (adds to Forge):** - Perform all Forge tier extractions (T1) - Additionally: query existing QMD temporal collections for each library - Read the `qmd_collections` registry from `{sidecar_path}/forge-tier.yaml` - For each library in `confirmed_dependencies`, search for a registry entry where `skill_name` matches the library name AND `type` is `"temporal"` - **If a matching temporal collection exists:** - Query `qmd_bridge.search("{library_name} deprecated OR removed OR breaking change")` for deprecation context - Query `qmd_bridge.search("{library_name} migration OR upgrade")` for migration patterns - Query `qmd_bridge.search("{library_name} version issue OR bug OR workaround")` for version-specific warnings - **Tool resolution for qmd_bridge:** Use QMD MCP tools — `mcp__plugin_qmd-plugin_qmd__search` (Claude Code), qmd MCP server (Cursor), `qmd search "{query}"` (CLI). See `knowledge/tool-resolution.md` - Classify each result as T2-past (historical) or T2-future (planned changes) per confidence-tiers.md - Append temporal findings to the library's extraction as T2 annotations with `[QMD:{collection}:{doc}]` citations - **If no matching temporal collection found:** - Log: "No temporal collection for {library_name}. T2 enrichment skipped." - Continue with T1/T1-low extraction only - Confidence: T1 for structural (AST), T2 for temporal annotations (QMD-enriched) ### 2. Launch Parallel Extraction **Launch subprocesses in parallel** (max_parallel_generation: 3–5 concurrent Agent tool calls in Claude Code, IDE-dependent in Cursor, CPU core count in CLI) — one per confirmed library: Each subprocess: 1. Reads all files importing the library (from step 03 file lists) 2. Extracts key exports used in this project (functions, classes, types, constants) 3. Identifies usage patterns (initialization, configuration, common call patterns) 4. Labels confidence tier based on extraction method 5. Returns structured extraction to parent: ``` { library: "name", version: "from_manifest", exports_found: ["fn1", "fn2", "Type1"], usage_patterns: ["pattern description with file:line"], confidence: "T1|T1-low|T2|T3", files_analyzed: count, warnings: [], temporal: { deprecated_exports: ["export_name — reason [QMD:collection:doc]"], migration_notes: ["note [QMD:collection:doc]"], version_warnings: ["warning [QMD:collection:doc]"], t2_annotation_count: count } } ``` **If parallel subprocess unavailable:** Process libraries sequentially in main thread. Report progress after each library. **Per-subprocess timeout (S6):** Apply a 60-second wall-clock timeout to each library's extraction subprocess. On timeout, mark the library as `partial-failure` with `warnings: ["extraction timeout after 60s"]`, store whatever partial data was returned (if any), and continue with the remaining libraries. Do NOT abort the batch on a single timeout. ### 3. Handle Extraction Failures For each library extraction: **Success:** Store extraction result. **Partial failure:** Store partial result with warnings, continue with other libraries. **Complete failure:** Log failure reason, exclude from stack skill, note in report. "**Warning:** Extraction failed for {library}: {reason}. Excluding from stack skill." **If ALL extractions fail:** HALT — cannot produce meaningful stack skill. Before halting (B7): 1. Purge any in-flight staging artifacts under the forge workspace: remove `{forge_data_folder}/{project_name}-stack/{version}/*-tmp` and any `{forge_data_folder}/{project_name}-stack/{version}/*.skf-tmp` directories so partial state does not linger. 2. Emit the result envelope on stderr per the Result Contract in SKILL.md (`stack_libraries` carries the confirmed library names that failed extraction), and exit `2`: ``` SKF_STACK_RESULT_JSON: {"status":"error","skill_package":null,"skill_name":"{project_name}-stack","stack_libraries":["<confirmed-lib>", "..."],"mode":"{code|compose}","exit_code":2,"halt_reason":"all-extractions-failed"} ``` ### 4. Display Extraction Summary Report the extraction results: per library the export count, pattern count, confidence tier, and success/partial status; the overall `{success_count}/{total_count}` extracted; and the T1 / T1-low / T2 confidence distribution. At Deep tier, add the T2-enrichment count (`{enriched_count}/{total_count}` libraries with temporal collections available); and if any library lacked a temporal collection, add the tip: run **[CS] Create Skill** at Deep tier for those libraries to generate temporal collections, then re-run **[SS]** for full T2 enrichment. Note any warning count. ### 5. Auto-Proceed to Next Step Load, read the full file and then execute `{nextStepFile}`. -
rank-and-confirm.md 6 KB
--- nextStepFile: 'parallel-extract.md' --- <!-- Config: communicate in {communication_language}. --> # Step 3: Rank and Confirm Scope ## STEP GOAL: Count import frequency for each dependency across the codebase, rank by usage, and present for user confirmation of which libraries to include in the stack skill. ## Rules - Focus on counting imports, ranking, and getting user confirmation — do not extract documentation (Step 04) - Use subprocess Pattern 1 (grep/search) when available ## MANDATORY SEQUENCE ### 1. Count Import Frequency **If `compose_mode` is true:** Skip import counting entirely. All skills are included by default. Set `confirmed_dependencies` = all `raw_dependencies` (the list already stored as workflow state from Step 02). **Apply scope_overrides:** If `scope_overrides` were provided in step 01, apply them now — force-include or force-exclude skills as specified. Log any overrides applied. **Validate override keys (S4):** Every key in `scope_overrides` MUST be present in `raw_dependencies`. For any unknown key, emit `"scope_override: unknown dependency '{key}' — skipping"` and drop that override entry (do not fail the run). Known-key overrides still apply. Present skills sorted by architectural layer (from architecture doc if available): - If `architecture_doc_path` is not null: **wrap the architecture-doc parse in try/except (S5)**. On any parse error (file unreadable, malformed markdown, no H2 structure), fall back to alphabetical ordering and log a warning `"architecture-doc parse failed: {error} — falling back to alphabetical"`. Otherwise parse section headers to determine layer grouping. - If `architecture_doc_path` is null or layers not detectable: present alphabetically. Display skills as a table: | # | Skill | Language | Tier | Architecture Layer | |---|-------|----------|------|--------------------| | 1 | {name} | {language} | {confidence_tier} | {layer or 'Unclassified'} | User confirms inclusion/exclusion at the gate (same [C] menu as code-mode). Skip to [Present MENU OPTIONS](#5-present-menu-options). **If not compose_mode:** For each dependency in `raw_dependencies`: **Launch a subprocess** that runs grep across all source files in the project to count import statements for each library. Return only the counts, not file contents. **Subprocess resolution:** Use the Grep tool (Claude Code), built-in search (Cursor), or `grep`/`rg` (CLI). See `knowledge/tool-resolution.md`. Use ecosystem-appropriate import patterns: - JavaScript/TypeScript: `import .* from ['"]library`, `require\(['"]library` - Python: `import library`, `from library import` - Rust: `use library::`, `extern crate library` - Go: `"library"` in import blocks - (Match patterns from `{manifestPatternsPath}`) **Subprocess returns:** `{library_name: import_count, files: [file_paths]}` for each dependency. **If subprocess unavailable:** Perform grep operations in main thread sequentially. Exclude from counting: - Test files (*/test/*, *_test.*, *.spec.*, *.test.*) - Config files (*.config.*, .eslintrc, etc.) - Build artifacts (dist/, build/, node_modules/, target/, __pycache__/) ### 2. Rank and Filter Sort dependencies by import count (descending). Apply filtering: - **Include by default:** Libraries with 2+ import files - **Flag as trivial:** Libraries with 0-1 import files - **Apply scope_overrides** from step 01 if provided (force include/exclude) ### 3. Present Ranked List "**Dependency ranking complete.** Here are your project's libraries ranked by usage: | # | Library | Imports | Files | Category | |---|---------|---------|-------|----------| | 1 | {name} | {count} | {file_count} | runtime | | 2 | {name} | {count} | {file_count} | runtime | | ... | ... | ... | ... | ... | **Below threshold** (0-1 imports — excluded by default): | Library | Imports | Category | |---------|---------|----------| | {name} | {count} | {category} | **Total:** {total} dependencies detected, {above_threshold} recommended for inclusion --- **Please confirm your scope:** - Type **C** to accept the recommended scope (all above-threshold libraries) - Type library names to **add** from the below-threshold list - Type **-library_name** to **exclude** a recommended library - Type a custom list to override entirely" ### 4. Process User Response **If C (accept recommended):** Store all above-threshold libraries as `confirmed_dependencies`. **If modifications requested:** Apply additions/exclusions, display updated list, and ask for final confirmation. **If custom list provided:** Use the custom list as `confirmed_dependencies`. Display the resolved scope: "**Scope confirmed:** {count} libraries selected for stack skill extraction. {List confirmed libraries}" The extraction itself begins only once the user clears the gate below. ### 5. Present MENU OPTIONS Display: **Select:** [C] Continue to Extraction | [X] Cancel and exit #### EXECUTION RULES: - This is a confirmation gate — advancing without the user's `C` would extract and ship a stack scope they never approved. Halt and wait for input after presenting scope. - **GATE [default: C]** — If `{headless_mode}`: auto-proceed with [C] Continue (accept all ranked libraries), log: "headless: auto-confirm library scope" - Proceed to the next step only once the user confirms scope by selecting `C`. #### Menu Handling Logic: - IF C: Store current `confirmed_dependencies` (including any modifications made since initial presentation), then load, read entire file, then execute {nextStepFile} - IF X: Invoke the rollback contract (purge any `{forge_data_folder}/{project_name}-stack/{version}/*-tmp` and `*.skf-tmp` staging artifacts under the forge workspace, leave any existing committed stack package untouched), emit the `SKF_STACK_RESULT_JSON` envelope on stderr with `status: "error"`, `halt_reason: "user-cancelled"`, `exit_code: 6`, and exit with code 6 - IF Any other: Process as scope modification (add/remove skills from `confirmed_dependencies`), update the in-memory `confirmed_dependencies` list accordingly, redisplay the updated skills table, then [Redisplay Menu Options](#5-present-menu-options) -
report.md 5.8 KB
--- nextStepFile: 'health-check.md' # Resolve `{atomicWriteHelper}` by probing `{atomicWriteProbeOrder}` in order # (installed SKF module path first, src/ dev-checkout fallback); first existing # path wins. HALT if neither resolves. atomicWriteProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py' - '{project-root}/src/shared/scripts/skf-atomic-write.py' --- <!-- Config: communicate in {communication_language}. Artifact text in {document_output_language}. --> # Step 9: Stack Skill Report ## STEP GOAL: Display the final summary of the forged stack skill with confidence distribution, output file listing, and next workflow recommendations. ## Rules - Do not write or modify any files — report is console output only - Lead with the positive summary, then details, then warnings - Recommend next workflows based on what was produced - Chains to the local health-check step via `{nextStepFile}` after completion — the user-facing report is NOT the terminal step ## MANDATORY SEQUENCE ### 1. Report the Forge Result Surface the forge result to the console, leading with the win: - **Headline:** stack `{project_name}-stack` — `{lib_count}` libraries, `{integration_count}` integration patterns, forge tier `{tier}`. - **Confidence distribution:** the T1 / T1-low / T2 counts (T1 = AST-verified structural extraction, T1-low = source-reading inference, T2 = QMD-enriched temporal context). **In compose-mode**, note the tiers are inherited from the source skills — they reflect the extraction method used when those skills were originally generated, not the current compose run. - **Output files:** the `{skill_package}` deliverables (SKILL.md, context-snippet.md with `{token_estimate}` tokens, metadata.json, `references/` per-library files, and `references/integrations/` pair files when integrations exist), the `{forge_version}` workspace (provenance-map.json, evidence-report.md), and the `{skill_group}/active -> {version}` symlink. - **Validation:** all checks passed, or `{warning_count}` finding(s) each with its description. - **Warnings — only if `workflow_warnings[]` is non-empty:** the accumulated entries rendered as `[{step}/{severity}] {code}: {message}`. `workflow_warnings[]` (defined in SKILL.md's *Workflow state contract*) is the single sink surfacing every warning pushed during the run; if it is empty, omit this section. ### 2. Recommend Next Workflows "**Next steps:** - **[TS] test-skill** — Validate the stack skill against its own assertions - **[EX] export-skill** — Package for distribution or agent loading - **[VS] verify-stack** — Validate the stack's integration feasibility against your architecture document{IF compose_mode:} (re-run to confirm feasibility after any architecture changes from **[RA] refine-architecture**){END IF}" ### 2b. Result Contract Write the result contract per `shared/references/output-contract-schema.md` using the shared atomic writer. Two artifacts — both written via `skf-atomic-write.py write`: **Per-run record (inside the version dir):** ```bash <json-content> | python3 {atomicWriteHelper} write \ --target {forge_version}/create-stack-skill-result-{YYYYMMDD-HHmmss}-{pid}-{rand}.json ``` - `{YYYYMMDD-HHmmss}` is a UTC timestamp with seconds resolution. - Append `-{pid}-{rand}` (process id + short random suffix) to the filename to avoid same-second collisions when multiple runs land in the same second (S16). **Stable latest pointer (ABOVE the version dir, at the stack group root):** ```bash <json-content> | python3 {atomicWriteHelper} write \ --target {forge_data_folder}/{project_name}-stack/create-stack-skill-result-latest.json ``` - Note the path: the `-latest.json` lives at `{forge_data_folder}/{project_name}-stack/` (the stack group root), NOT inside `{forge_version}/`. Pipeline consumers read this stable path without knowing the current version. - Write the same JSON body as the timestamped record (this is a copy, not a symlink, so pipeline consumers never chase a link across version boundaries). Include `SKILL.md`, `context-snippet.md`, and `metadata.json` paths in `outputs`; include `lib_count`, `integration_count`, `forge_tier`, `confidence_tier`, and confidence distribution in `summary`. If either atomic write fails, log the error, leave any prior `-latest.json` untouched, and continue — the report is advisory and should not block the health-check chain. **Headless success envelope.** When `{headless_mode}` is true, emit the single-line result envelope on **stdout** (the success counterpart to the error envelopes every HARD HALT emits on stderr) before chaining to step 10. `skill_package` is the absolute path to the committed package; `stack_libraries` is the included library names: ``` SKF_STACK_RESULT_JSON: {"status":"success","skill_package":"{skill_package}","skill_name":"{project_name}-stack","stack_libraries":["<lib>", "..."],"mode":"{code|compose}","exit_code":0,"halt_reason":null} ``` ### 2c. Post-Completion Hook (optional) If `{onCompleteCommand}` (resolved at SKILL.md On Activation §3 from `workflow.on_complete`) is non-empty, invoke it now — after the result contract (§2b) is written, before chaining to health-check: ```bash {onCompleteCommand} ``` Run it with a bounded timeout (default 60s). On success, continue. On non-zero exit, timeout, or any failure, append the reason to `workflow_warnings[]` (e.g. `on_complete — failed (exit {N}): {stderr_first_line}`) and continue. **The hook must never fail the workflow** — it is integration glue (catalog registration, downstream pipeline notify) orthogonal to the forged stack. When `{onCompleteCommand}` is empty (bundled default), skip this section entirely. ### 3. Chain to Health Check After the report sections above are handled, load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the report reads as final. -
validate.md 11.9 KB
--- nextStepFile: 'report.md' outputValidatorProbeOrder: - '{project-root}/_bmad/skf/shared/scripts/skf-validate-output.py' - '{project-root}/src/shared/scripts/skf-validate-output.py' --- <!-- Config: communicate in {communication_language}. Artifact text in {document_output_language}. --> # Step 8: Validate Output ## STEP GOAL: Validate all written output files against their expected structure and verify confidence tier label completeness. ## Rules - Validate structure and completeness, not content quality — validation is read-only - Advisory mode: always proceed to report regardless of findings ## MANDATORY SEQUENCE ### 1. Verify File Existence Run the shared deterministic output validator once against the committed package — it checks the three core deliverable files' existence, SKILL.md frontmatter, and context-snippet format/token in a single call, so this step consumes its JSON rather than re-deriving those by hand. Resolve `{outputValidator}` from `{outputValidatorProbeOrder}` (first existing path wins). If neither candidate exists, log a WARNING (`"output validator unavailable — skf-validate-output.py missing"`) and fall back to the manual file/frontmatter/snippet checks below. ```bash python3 {outputValidator} {skill_package} --generated-by create-stack-skill --skill-type stack ``` Consume from its JSON: - `files_found` — existence of `SKILL.md` / `context-snippet.md` / `metadata.json` (the three core deliverable rows below). - `validation.skill_md.frontmatter` — feeds the frontmatter check in §3 (manual-fallback path). - `validation.context_snippet.issues` — feeds §8. - `validation.stack_counts` — the stack count-equalities, derived deterministically from disk + metadata by the `--skill-type stack` pass: `issues[]` (each `{severity, field, message}`, one per mismatch of `library_count` / `integration_count` / `confidence_distribution`; empty when all agree) and `observed` (`library_count_meta`, `ref_file_count`, `integration_count_meta`, `pair_file_count`, `confidence_sum`). Feeds the count rows in §5 and the confidence-sum row in §7 — do not re-count files or re-sum the distribution by hand. **Under `--skill-type stack`, `validation.skill_md.body` and `validation.metadata` are emitted as `{"skipped": ...}` markers** — the *individual-skill* schema (which looks for `## Overview` / `Description` / `Key Exports` / `Usage` sections and a `source_repo` field) does not apply to a stack package, so the validator skips those passes and runs `validation.stack_counts` instead. Stack-shaped body structure and the remaining (non-count) metadata fields are checked in §4 / §5. Then confirm the remaining files the validator does not cover: **Deliverables** (`{skill_package}`): - [ ] SKILL.md · context-snippet.md · metadata.json — from `files_found` above - [ ] references/ directory with per-library files - [ ] references/integrations/ directory with pair files (if integrations detected) **Workspace** (`{forge_version}`): - [ ] provenance-map.json - [ ] evidence-report.md **Symlink:** - [ ] `{skill_group}/active` exists and resolves to `{version}` Record any missing files (from `files_found` or the manual rows) as **ERROR** findings. ### 2. Check Tool Availability Probe skill-check with `--no-install` to avoid cold-install hangs, wrap in a short timeout, and treat any hang or non-zero exit as unavailable (S14): ```bash timeout 10s npx --no-install skill-check -h ``` - If exits 0: Use skill-check for automated validation in sections 3, 9. - If exits non-zero, times out, or returns "command not found": Use manual fallback paths. Mark `metadata.validation_status: "manual-only"` (do this in step 7 when appropriate) and record every skipped check in the evidence report. **Important:** Do not assume availability — empirical check required. ### 3. Validate SKILL.md via skill-check (if available) **If available**, run: `npx skill-check check <skill-dir> --fix --format json --no-security-scan` This validates frontmatter, description, body limits, links, formatting — and auto-fixes deterministic issues. Parse JSON for `scores[].score` (match the entry by `relativePath`/`skillId`; falls back to a top-level `qualityScore` on older skill-check builds), `diagnostics[]`, `fixed[]`. **Post-fix provenance drift guard (S15):** If `fixed[]` is non-empty, `skill-check --fix` has modified `SKILL.md` after step 7 wrote it — so the `metadata.json` hashes/provenance recorded against the pre-fix body may now be stale. Emit a **WARNING** finding listing each auto-fix (`"skill-check --fix modified SKILL.md: {fix_description} — metadata.json hashes/provenance may be out of date"`) rather than silently accepting the fixes, so the drift is surfaced. If the caller wants authoritative metadata, they should re-run the workflow. **If `body.max_lines` reported**, prefer selective split: extract only the largest Tier 2 section(s) to `references/`, keeping Tier 1 content inline (inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval). For a stack capstone the canonical split is the catalog (`Library Reference Index` + `Per-Library Summaries`) → `references/stack-catalog.md`, leaving an inline pointer (see `{stackSkillTemplatePath}` "Sizing Guidance"). This is the **intended** large-stack layout, not a violation: §4 below accepts the pointer form, so clearing the skill-check body ERROR this way does not also trip the structure check. Fall back to `npx skill-check split-body <skill-dir> --write` if not feasible. Verify any in-SKILL.md anchor links (e.g. to the catalog/pointer or other moved sections) still resolve after the split. Then re-validate. **If unavailable**, do not hand-walk the frontmatter — use `validation.skill_md.frontmatter` from the §1 output-validator run, which checks delimiters, `name` format + directory match (`{project_name}-stack`), `description` presence/length, and unknown fields against the agentskills.io allow-set. Record each reported issue at its severity as a **WARNING** finding. (If the output validator was *also* unavailable in §1, fall back to the manual checklist: `---` delimiters; `name` lowercase-alphanumeric-plus-hyphens 1-64 chars matching `{project_name}-stack`; `description` present and 1-1024 chars; only `name`/`description`/`license`/`compatibility`/`metadata`/`allowed-tools` permitted.) Invalid frontmatter will fail `npx skills add` and `npx skill-check check`. ### 4. Validate SKILL.md Body Structure Load `{stackSkillTemplatePath}` and verify SKILL.md contains expected sections: - [ ] Header with project name, library count, integration count, forge tier - [ ] Integration Patterns section (before per-library summaries) - [ ] Conventions section - [ ] **Catalog** — `Library Reference Index` table + `Per-Library Summaries`, in **either** form: - *Inline* (small stacks): both sections present in SKILL.md, **or** - *Pointer* (large stacks): a `Library Catalog` pointer to `references/stack-catalog.md`, **and** that file exists and contains both sections. Accept either form — do not WARN when the catalog has been extracted to clear the `body.max_lines` budget (§3). Only record a **WARNING** when *neither* the inline sections *nor* a pointer-plus-`stack-catalog.md` is present, or when the pointer's target file is missing. Record other missing sections (Header, Integration Patterns, Conventions) as **WARNING** findings. ### 5. Validate metadata.json Fields Parse metadata.json and verify required fields: - [ ] `skill_type` equals "stack" - [ ] `name` matches `{project_name}-stack` - [ ] `version` and `generation_date` present - [ ] `forge_tier` is present and matches the forge tier from step 01 (`Quick|Forge|Forge+|Deep`) - [ ] `confidence_tier` is present and is exactly one of `T1|T1-low|T2|T3` — the dominant T-code computed from `confidence_distribution` (pick the tier with the highest count; ties resolve to the weaker tier: T1-low > T1, T2 > T1-low, T3 > T2 for tie-break so the reported tier never overstates confidence) - [ ] `libraries` array present and non-empty - [ ] `confidence_distribution` object present with `t1`, `t1_low`, `t2`, `t3` keys (lowercase, matching template definition) **Count equalities (library / integration)** — do NOT re-count files here; take them from `validation.stack_counts` in the §1 output-validator run (invoked with `--skill-type stack`), which derived them deterministically from disk. The `library_count` vs per-library reference files and `integration_count` vs integration pair files checks surface as `field: "library_count"` / `field: "integration_count"` entries in `validation.stack_counts.issues[]` (absent when they agree); echo the exact numbers from `validation.stack_counts.observed` (`library_count_meta` / `ref_file_count`, `integration_count_meta` / `pair_file_count`). The `confidence_distribution`-sum equality is covered in §7. Record each `validation.stack_counts.issues[]` count entry (`library_count` / `integration_count`) and any other mismatch above as **WARNING** findings. ### 6. Validate Reference File Completeness For each confirmed library, verify `references/{library}.md` contains: library name header, version from manifest (**in compose-mode**: version from source skill metadata), Key Exports section, Usage Patterns section. For each integration pair, verify `references/integrations/{libraryA}-{libraryB}.md` contains: integration pair header, type classification, Integration Pattern section, Key Files section. Record missing or incomplete files as **WARNING** findings. ### 7. Validate Confidence Tier Labels Scan all output files for confidence tier coverage: - [ ] SKILL.md: each per-library summary and integration pair entry has a confidence label - [ ] Reference files: each has a confidence label in its header - [ ] metadata.json: `confidence_distribution` sums to `library_count` — take this from the §1 output-validator run (`--skill-type stack`): the `field: "confidence_distribution"` entry in `validation.stack_counts.issues[]` is present only on mismatch, with `validation.stack_counts.observed.confidence_sum` vs `library_count_meta` for the exact numbers. Do not re-sum the distribution by hand. Record missing tier labels and any `validation.stack_counts` `confidence_distribution` issue as **WARNING** findings. ### 8. Validate context-snippet.md Take the first-line format, `|IMPORTANT:` second-line, and token-estimate checks from `validation.context_snippet.issues` returned by the §1 output-validator run — it performs the line-1 `[name vVersion]|root:` pattern match, the line-2 check, and the `len(content)//4` token estimate deterministically, so this step does not recompute them. Record each reported issue as a **WARNING** finding. Then verify the two stack-specific rows the generic validator does not cover: - [ ] Stack and integrations lines present - [ ] Token estimate lands near the ~80-120 design target from step 7 §5 (the validator flags only its wider <40 / >200 bounds; an ~80-150 snippet with an overflow-strategy `workflow_warning` is expected, not a defect) Record format violations as **WARNING** findings. ### 9. Security Scan (if skill-check available) Run: `npx skill-check check <skill-dir> --format json` (security scan enabled by default). Record security findings as advisory **WARNING** findings — they do not block the report. **If unavailable:** Skip with note in validation results. ### 10. Display Validation Results Report the validation outcome. If all checks passed, state so and name what was verified: file presence (`{count}/{count}`), SKILL.md structure, metadata.json fields, the `{lib_count}` library + `{pair_count}` integration reference files, and complete confidence-tier coverage. If there were findings, report the `{warning_count}` finding(s) — each with severity, description, and file path — plus files present/expected and warning/error counts; when errors include missing files, note this may indicate a write failure in step 07. ### 11. Auto-Proceed to Next Step Load, read the full file and then execute `{nextStepFile}`.
-
-
customize.toml 2.2 KB
# DO NOT EDIT -- overwritten on every update. # # Workflow customization surface for skf-create-stack-skill. # Team overrides: _bmad/custom/skf-create-stack-skill.toml (under {project-root}) # Personal overrides: _bmad/custom/skf-create-stack-skill.user.toml (under {project-root}) [workflow] # --- Configurable below. Overrides merge per BMad structural rules: --- # scalars: override wins • arrays (persistent_facts, activation_steps_*): append # arrays-of-tables with `code`/`id`: replace matching items, append new ones. # Steps to run before the standard activation (uv probe, config load). # Overrides append. Use for org-wide pre-flight checks (auth, network, # compliance) that must precede any stack compilation work. activation_steps_prepend = [] # Steps to run after activation but before the first stage executes. # Overrides append. Use for context loads or banner customization that # should run once activation completes successfully. activation_steps_append = [] # Persistent facts the workflow keeps in mind for the whole run # (house style, naming conventions, integration-pattern guardrails). # Overrides append. # # Each entry is either: # - a literal sentence, e.g. "Stack skills must cite their constituent libraries." # - a file reference prefixed with `file:`, e.g. # "file:{project-root}/docs/stack-style.md" (globs supported; file # contents are loaded and treated as facts). persistent_facts = [ "file:{project-root}/**/project-context.md", ] # Command invoked once the workflow reaches its terminal stage — after the # stack package is committed and the result contract is written (step 9, # report.md §6c), before the health-check chain. Override wins. Use for a # terminal action such as catalog registration or notifying a downstream # pipeline. Empty string = no terminal action. The hook never fails the run. on_complete = "" # --- Optional asset overrides --- # # Lift the canonical asset paths so orgs can substitute house-style copies # without forking the skill. Empty string = use the bundled default. stack_skill_template_path = "" integration_patterns_path = "" manifest_patterns_path = "" compose_mode_rules_path = "" provenance_map_schema_path = "" -
SKILL.md 10.4 KB
--- name: skf-create-stack-skill description: Consolidated project stack skill with integration patterns — code-mode (analyzes manifests) or compose-mode (synthesizes from existing skills + architecture doc). Use when the user requests to "create a stack skill", "forge a stack", or "stack this project". --- # Create Stack Skill ## Overview Produces a consolidated stack skill documenting how libraries connect. **Code-mode** analyzes dependency manifests and co-import patterns from actual source code. **Compose-mode** synthesizes from pre-generated individual skills and architecture documents when no codebase exists yet. Every finding must trace to actual code with file:line citations; in compose-mode, inferred integrations are permitted but must be labeled `[inferred from shared domain]`. ## Conventions - Bare paths (e.g. `references/<name>.md`) resolve from the skill root. - The `knowledge/` and `shared/` prefixes are the exception: they resolve from the **SKF module root** (`{project-root}/_bmad/skf/` when installed, `src/` during development), not the skill root — they point at module-shared reference docs (`knowledge/tool-resolution.md`, `knowledge/version-paths.md`) and scripts/schemas (`shared/references/…`) that live once at the module root, mirroring the resolution note `references/health-check.md` carries for `shared/health-check.md`. - `references/` holds prompt content carved out of SKILL.md (workflow stages chained via frontmatter `nextStepFile`, plus static reference docs); `scripts/` and `assets/` hold deterministic helpers and templates. - `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present). - `{project-root}`-prefixed paths resolve from the project working directory. - `{skill-name}` resolves to the skill directory's basename. ## Role You are a dependency analyst and integration architect. You bring expertise in dependency analysis, cross-library integration patterns, and compositional architecture, while the user brings their project knowledge and scope preferences. ## Workflow Rules These rules apply to every step in this workflow: - Zero hallucination — all extracted content must trace to actual source code (compose-mode inferences must be labeled) - Only load one step file at a time — never preload future steps - If any instruction references a subprocess or tool you lack, achieve the outcome in your main context thread - Always communicate in `{communication_language}` - If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision - Warnings use a single accumulator — see `## Workflow state contract` below for shape and surfacing. ## Workflow state contract Every step that emits a warning appends a structured entry to a single in-memory list named `workflow_warnings[]` (the one accumulator for the whole workflow). Each entry has the shape `{step: "step-NN", severity: "info|warn|error", code: "<short-slug>", message: "<human text>", context: {<optional fields>}}`. Step 7 surfaces these in `evidence-report.md`; step 8 may add validation findings; step 9 §5 reads the accumulated list and renders the user-facing "Warnings" section. **Single-pass — no mid-run checkpoint.** State lives in memory until step 7 commits `provenance-map.json`/`evidence-report.md`; the workflow keeps no resumable checkpoint and On Activation does not probe for a prior run — the analysis is deterministic and cheap to redo, and the two gates are trivially re-confirmed. If interrupted before that commit, restart from step 1. ## Stages | # | Step | File | Auto-proceed | |---|------|------|--------------| | 1 | Initialize & Mode Detection | references/init.md | No (confirm) | | 2 | Detect Manifests | references/detect-manifests.md | Yes | | 3 | Rank & Confirm Libraries | references/rank-and-confirm.md | No (confirm) | | 4 | Parallel Extract | references/parallel-extract.md | Yes | | 5 | Detect Integrations | references/detect-integrations.md | Yes | | 6 | Compile Stack | references/compile-stack.md | No (review) | | 7 | Generate Output | references/generate-output.md | Yes | | 8 | Validate | references/validate.md | Yes | | 9 | Report | references/report.md | Yes | | 10 | Workflow Health Check | references/health-check.md | Yes | ## Invocation Contract | Aspect | Detail | |--------|--------| | **Inputs** | project_path [required], mode (code/compose) [auto-detected] | | **Gates** | step 3: Confirm Gate [C] | step 6: Review Gate [C] | | **Outputs** | SKILL.md (stack), context-snippet.md, metadata.json | | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true | | **Exit codes** | See "Exit Codes" below | ## Exit Codes Every HARD HALT in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text: | Code | Meaning | Raised by (halt_reason) | | ---- | -------------------- | ------------------------------------------------------------------------------------------ | | 0 | success | step 10 (terminal handoff to shared health-check) | | 2 | input / precondition invalid | step 1 §0 `config.yaml` missing/malformed (`config-missing`); step 2 §2 headless with no manifests (`no-manifests`, S2); step 4 §3 all extractions failed (`all-extractions-failed`, B7); step 5 §2 feasibility-report `schemaVersion` mismatch (`schema-version-mismatch`) | | 3 | resolution-failure | step 1 §1 `forge-tier.yaml` missing (`forge-tier-missing`); step 2 §0 compose-mode skill-resolution corruption (manifest + symlink both fail); step 2 §0 compose-mode zero qualifying skills (S1/B4); step 4 §0 compose-cycle — all `resolution-failure` | | 4 | write-failure | step 7 §1 stage-dir / commit-dir failure; step 7 §1 group-dir collision when an existing non-stack skill occupies the target path — both `write-failure` | | 6 | user-cancelled | any interactive menu in step 3 / step 6 when the user selects `[X]` Cancel and exit (`user-cancelled`) | ## Result Contract (Headless) When `{headless_mode}` is true, step 9 emits a single-line JSON envelope on **stdout** before chaining to step 10, and every HARD HALT emits the same envelope shape on **stderr** with `status: "error"`: ``` SKF_STACK_RESULT_JSON: {"status":"success|error","skill_package":"…|null","skill_name":"…","stack_libraries":["…"],"mode":"code|compose","exit_code":0,"halt_reason":null} ``` `status` is `"success"` on the terminal happy path, `"error"` on any HALT. `skill_package` is the absolute path to the committed stack-skill directory (or `null` on error before commit). `skill_name` is the stack skill's published name (e.g. `{project_name}-stack`). `stack_libraries` is the array of library names included in the stack (constituent skill names in compose-mode, dependency names in code-mode). `mode` is `"code"` or `"compose"` per the run's resolved mode (`null` if the run halts before mode resolution). `halt_reason` is one of: `null` (success), `"config-missing"`, `"forge-tier-missing"`, `"no-manifests"`, `"all-extractions-failed"`, `"schema-version-mismatch"`, `"resolution-failure"`, `"write-failure"`, `"user-cancelled"`. `exit_code` matches the table above. Fields unknown at the halt point are `null` (`skill_name`) or `[]` (`stack_libraries`) — e.g. a `config-missing` halt precedes `project_name` resolution. ## On Activation 1. Load config from `{project-root}/_bmad/skf/config.yaml` and resolve: - `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`, `skills_output_folder`, `forge_data_folder`, `sidecar_path` 2. **Resolve `{headless_mode}`** with explicit precedence (B2): 1. **Explicit disable wins.** If `--headless=false` or `--no-headless` was passed, `{headless_mode}` is `false` regardless of any preference. 2. **Explicit enable next.** If `--headless` or `-H` was passed (without `=false`), `{headless_mode}` is `true`. 3. **Preferences fallback.** Otherwise, read `headless_mode` from `{sidecar_path}/preferences.yaml` (`true` or `false`). 4. **Default:** `false`. 3. **Resolve workflow customization.** Run: ```bash python3 {project-root}/_bmad/scripts/resolve_customization.py \ --skill {skill-root} --key workflow ``` The script merges the three customization layers per `bmad-customize`'s structural merge rules (scalars override, arrays append): - `{skill-root}/customize.toml` — bundled defaults - `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed) - `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored) If the script fails or is missing, fall back to reading `{skill-root}/customize.toml` directly — the bundled defaults are an empty string for each path scalar. Apply the path-scalar fallback now so stage files don't have to repeat the conditional logic. For each of the five scalars, if the merged value is empty or absent, use the bundled default: - `{stackSkillTemplatePath}` ← `workflow.stack_skill_template_path` if non-empty, else `assets/stack-skill-template.md` - `{integrationPatternsPath}` ← `workflow.integration_patterns_path` if non-empty, else `references/integration-patterns.md` - `{manifestPatternsPath}` ← `workflow.manifest_patterns_path` if non-empty, else `references/manifest-patterns.md` - `{composeModeRulesPath}` ← `workflow.compose_mode_rules_path` if non-empty, else `references/compose-mode-rules.md` - `{provenanceMapSchemaPath}` ← `workflow.provenance_map_schema_path` if non-empty, else `assets/provenance-map-schema.md` Also resolve `{onCompleteCommand}` ← `workflow.on_complete` if non-empty, else empty string (no-op — `references/report.md` §6c skips the hook invocation entirely). Stash all five paths plus `{onCompleteCommand}` as workflow-context variables. Stage files reference `{stackSkillTemplatePath}` / `{integrationPatternsPath}` / `{manifestPatternsPath}` / `{composeModeRulesPath}` / `{provenanceMapSchemaPath}` directly; empty-string overrides fall through to the bundled default. Also apply the array surfaces: run `workflow.activation_steps_prepend` now, keep `workflow.persistent_facts` as standing context (`file:` entries load their contents), then run `workflow.activation_steps_append` after. 4. Load, read the full file, and then execute `references/init.md` to begin the workflow.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.