bmad-module-skill-forge 1.9.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.nvmrc +1 -1
- package/README.md +10 -5
- package/docs/_data/pinned.yaml +2 -2
- package/docs/agents.md +11 -2
- package/docs/architecture.md +5 -4
- package/docs/bmad-synergy.md +30 -7
- package/docs/campaign.md +172 -0
- package/docs/examples.md +7 -3
- package/docs/forge-auto.md +90 -0
- package/docs/getting-started.md +9 -0
- package/docs/how-it-works.md +6 -4
- package/docs/index.md +4 -3
- package/docs/skill-model.md +1 -1
- package/docs/troubleshooting.md +17 -1
- package/docs/verifying-a-skill.md +2 -2
- package/docs/why-skf.md +1 -1
- package/docs/workflows.md +64 -24
- package/package.json +2 -2
- package/src/knowledge/skill-lifecycle.md +1 -1
- package/src/module-help.csv +4 -3
- package/src/shared/data/language-corpora.json +32 -0
- package/src/shared/references/pipeline-contracts.md +6 -3
- package/src/shared/scripts/schemas/skill-brief.v1.json +6 -1
- package/src/shared/scripts/skf-comention-pairs.py +350 -0
- package/src/shared/scripts/skf-count-tokens.py +222 -0
- package/src/shared/scripts/skf-derive-assembly-shape.py +107 -0
- package/src/shared/scripts/skf-detect-docs.py +240 -4
- package/src/shared/scripts/skf-detect-language.py +82 -3
- package/src/shared/scripts/skf-detect-tools.py +105 -2
- package/src/shared/scripts/skf-emit-result-envelope.py +17 -0
- package/src/shared/scripts/skf-enumerate-stack-skills.py +111 -2
- package/src/shared/scripts/skf-extract-public-api.py +53 -0
- package/src/shared/scripts/skf-find-cycles.py +246 -0
- package/src/shared/scripts/skf-hash-content.py +487 -2
- package/src/shared/scripts/skf-language-corpora.py +100 -0
- package/src/shared/scripts/skf-manifest-ops.py +67 -1
- package/src/shared/scripts/skf-merge-doc-urls.py +244 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-rebuild-managed-sections.py +180 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +1 -2
- package/src/shared/scripts/skf-render-metadata-stats.py +499 -0
- package/src/shared/scripts/skf-rewrite-skill-name.py +308 -0
- package/src/shared/scripts/skf-shape-detect.py +576 -25
- package/src/shared/scripts/skf-shard-body.py +461 -0
- package/src/shared/scripts/skf-skill-inventory.py +139 -3
- package/src/shared/scripts/skf-structural-diff.py +307 -67
- package/src/shared/scripts/skf-validate-brief-schema.py +41 -6
- package/src/shared/scripts/skf-validate-feasibility-report.py +234 -0
- package/src/shared/scripts/skf-validate-frontmatter.py +17 -12
- package/src/shared/scripts/skf-validate-output.py +443 -10
- package/src/shared/scripts/skf-validate-pins.py +3 -3
- package/src/shared/scripts/skf-verify-no-trace.py +214 -0
- package/src/shared/scripts/skf-verify-provenance-completeness.py +439 -0
- package/src/shared/scripts/skf-write-skill-brief.py +21 -5
- package/src/skf-analyze-source/SKILL.md +17 -32
- package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
- package/src/skf-analyze-source/customize.toml +2 -0
- package/src/skf-analyze-source/references/auto-docs-only.md +116 -0
- package/src/skf-analyze-source/references/continue.md +8 -13
- package/src/skf-analyze-source/references/generate-briefs.md +28 -12
- package/src/skf-analyze-source/references/headless-contract.md +33 -0
- package/src/skf-analyze-source/references/health-check.md +1 -2
- package/src/skf-analyze-source/references/identify-units.md +25 -36
- package/src/skf-analyze-source/references/init.md +12 -25
- package/src/skf-analyze-source/references/map-and-detect.md +8 -20
- package/src/skf-analyze-source/references/recommend.md +4 -14
- package/src/skf-analyze-source/references/scan-project.md +3 -11
- package/src/skf-analyze-source/references/step-auto-scope.md +211 -197
- package/src/skf-analyze-source/references/step-shape-detect.md +20 -13
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -2
- package/src/skf-audit-skill/SKILL.md +9 -6
- package/src/skf-audit-skill/customize.toml +2 -0
- package/src/skf-audit-skill/references/init.md +58 -32
- package/src/skf-audit-skill/references/re-index.md +15 -34
- package/src/skf-audit-skill/references/report.md +11 -58
- package/src/skf-audit-skill/references/semantic-diff.md +4 -22
- package/src/skf-audit-skill/references/severity-classify.md +53 -87
- package/src/skf-audit-skill/references/step-doc-drift.md +49 -19
- package/src/skf-audit-skill/references/structural-diff.md +43 -68
- package/src/skf-brief-skill/SKILL.md +7 -36
- package/src/skf-brief-skill/assets/scope-templates.md +1 -1
- package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -1
- package/src/skf-brief-skill/customize.toml +14 -0
- package/src/skf-brief-skill/references/analyze-target.md +13 -18
- package/src/skf-brief-skill/references/confirm-brief.md +2 -15
- package/src/skf-brief-skill/references/draft-checkpoint.md +8 -8
- package/src/skf-brief-skill/references/gather-intent.md +27 -24
- package/src/skf-brief-skill/references/headless-args.md +1 -1
- package/src/skf-brief-skill/references/health-check.md +1 -22
- package/src/skf-brief-skill/references/invocation-contract.md +37 -0
- package/src/skf-brief-skill/references/scope-definition.md +6 -15
- package/src/skf-brief-skill/references/step-auto-brief.md +22 -9
- package/src/skf-brief-skill/references/step-auto-validate.md +3 -7
- package/src/skf-brief-skill/references/version-resolution.md +1 -1
- package/src/skf-brief-skill/references/write-brief.md +19 -13
- package/src/skf-campaign/SKILL.md +121 -0
- package/src/skf-campaign/assets/campaign-state-schema.json +191 -0
- package/src/skf-campaign/customize.toml +75 -0
- package/src/skf-campaign/manifest.yaml +15 -0
- package/src/skf-campaign/references/campaign-contracts.md +67 -0
- package/src/skf-campaign/references/campaign-directive-spec.md +32 -0
- package/src/skf-campaign/references/health-check.md +35 -0
- package/src/skf-campaign/references/step-01-setup.md +121 -0
- package/src/skf-campaign/references/step-02-strategy.md +102 -0
- package/src/skf-campaign/references/step-03-pins.md +56 -0
- package/src/skf-campaign/references/step-04-provenance.md +63 -0
- package/src/skf-campaign/references/step-05-skill-loop.md +103 -0
- package/src/skf-campaign/references/step-06-batch.md +88 -0
- package/src/skf-campaign/references/step-07-capstone.md +63 -0
- package/src/skf-campaign/references/step-08-verify.md +75 -0
- package/src/skf-campaign/references/step-09-refine.md +83 -0
- package/src/skf-campaign/references/step-10-export.md +106 -0
- package/src/skf-campaign/references/step-11-maintenance.md +84 -0
- package/src/skf-campaign/references/step-resume.md +115 -0
- package/src/skf-campaign/scripts/.gitkeep +0 -0
- package/src/skf-campaign/scripts/campaign-deps.py +244 -0
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +119 -0
- package/src/skf-campaign/scripts/campaign-provenance.py +247 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +199 -0
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +158 -0
- package/src/skf-campaign/scripts/campaign-report.py +249 -0
- package/src/skf-campaign/scripts/campaign-status.py +162 -0
- package/src/skf-campaign/scripts/campaign-validate-pins.py +174 -0
- package/src/skf-campaign/scripts/campaign-validate-state.py +207 -0
- package/src/skf-campaign/templates/campaign-brief-template.yaml +34 -0
- package/src/skf-campaign/templates/campaign-report-template.md +54 -0
- package/src/skf-campaign/templates/kickoff-template.md +48 -0
- package/src/skf-create-skill/SKILL.md +15 -4
- package/src/skf-create-skill/assets/compile-assembly-rules.md +35 -34
- package/src/skf-create-skill/assets/skill-sections.md +9 -100
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +4 -13
- package/src/skf-create-skill/customize.toml +13 -0
- package/src/skf-create-skill/references/authoritative-files-protocol.md +7 -7
- package/src/skf-create-skill/references/compile.md +37 -47
- package/src/skf-create-skill/references/component-extraction.md +8 -35
- package/src/skf-create-skill/references/ecosystem-check.md +9 -25
- package/src/skf-create-skill/references/enrich.md +4 -17
- package/src/skf-create-skill/references/extract.md +20 -27
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +1 -1
- package/src/skf-create-skill/references/extraction-patterns.md +10 -8
- package/src/skf-create-skill/references/generate-artifacts.md +20 -25
- package/src/skf-create-skill/references/health-check.md +2 -2
- package/src/skf-create-skill/references/load-brief.md +16 -18
- package/src/skf-create-skill/references/report.md +27 -14
- package/src/skf-create-skill/references/source-resolution-protocols.md +4 -4
- package/src/skf-create-skill/references/step-auto-shard.md +52 -25
- package/src/skf-create-skill/references/step-doc-rot.md +33 -15
- package/src/skf-create-skill/references/step-doc-sources.md +13 -4
- package/src/skf-create-skill/references/sub/ccc-discover.md +2 -13
- package/src/skf-create-skill/references/sub/fetch-docs.md +33 -46
- package/src/skf-create-skill/references/sub/fetch-temporal.md +5 -20
- package/src/skf-create-skill/references/tier-degradation-rules.md +6 -8
- package/src/skf-create-skill/references/validate.md +69 -29
- package/src/skf-create-skill/scripts/scan-doc-rot.py +245 -0
- package/src/skf-create-stack-skill/SKILL.md +15 -9
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +1 -1
- package/src/skf-create-stack-skill/customize.toml +10 -0
- package/src/skf-create-stack-skill/references/compile-stack.md +7 -8
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -13
- package/src/skf-create-stack-skill/references/detect-integrations.md +49 -54
- package/src/skf-create-stack-skill/references/detect-manifests.md +37 -30
- package/src/skf-create-stack-skill/references/generate-output.md +24 -75
- package/src/skf-create-stack-skill/references/init.md +9 -81
- package/src/skf-create-stack-skill/references/manifest-patterns.md +5 -35
- package/src/skf-create-stack-skill/references/parallel-extract.md +12 -27
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +6 -6
- package/src/skf-create-stack-skill/references/report.md +27 -62
- package/src/skf-create-stack-skill/references/validate.md +35 -31
- package/src/skf-drop-skill/SKILL.md +20 -23
- package/src/skf-drop-skill/customize.toml +10 -0
- package/src/skf-drop-skill/references/execute.md +67 -36
- package/src/skf-drop-skill/references/headless-contract.md +18 -0
- package/src/skf-drop-skill/references/health-check.md +1 -13
- package/src/skf-drop-skill/references/report.md +44 -10
- package/src/skf-drop-skill/references/select.md +93 -48
- package/src/skf-drop-skill/scripts/dir-sizes.py +116 -0
- package/src/skf-export-skill/SKILL.md +17 -14
- package/src/skf-export-skill/assets/managed-section-format.md +2 -5
- package/src/skf-export-skill/customize.toml +10 -6
- package/src/skf-export-skill/references/generate-snippet.md +21 -35
- package/src/skf-export-skill/references/load-skill.md +44 -42
- package/src/skf-export-skill/references/multi-skill-mode.md +1 -1
- package/src/skf-export-skill/references/orphan-context-detection.md +11 -3
- package/src/skf-export-skill/references/package.md +27 -33
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +24 -5
- package/src/skf-export-skill/references/result-envelope.md +28 -0
- package/src/skf-export-skill/references/summary.md +11 -5
- package/src/skf-export-skill/references/token-report.md +42 -24
- package/src/skf-export-skill/references/update-context.md +32 -33
- package/src/skf-forger/SKILL.md +32 -39
- package/src/skf-forger/references/pipeline-mode.md +34 -0
- package/src/skf-forger/scripts/parse-pipeline.py +270 -0
- package/src/skf-quick-skill/SKILL.md +10 -60
- package/src/skf-quick-skill/customize.toml +9 -0
- package/src/skf-quick-skill/references/batch-mode.md +6 -4
- package/src/skf-quick-skill/references/compile.md +15 -52
- package/src/skf-quick-skill/references/ecosystem-check.md +5 -20
- package/src/skf-quick-skill/references/finalize.md +12 -6
- package/src/skf-quick-skill/references/halt-contract.md +54 -0
- package/src/skf-quick-skill/references/health-check.md +3 -4
- package/src/skf-quick-skill/references/quick-extract.md +7 -18
- package/src/skf-quick-skill/references/registry-resolution.md +1 -23
- package/src/skf-quick-skill/references/resolve-target.md +27 -34
- package/src/skf-quick-skill/references/write-and-validate.md +10 -25
- package/src/skf-refine-architecture/SKILL.md +16 -8
- package/src/skf-refine-architecture/customize.toml +9 -0
- package/src/skf-refine-architecture/references/compile.md +10 -8
- package/src/skf-refine-architecture/references/gap-analysis.md +21 -50
- package/src/skf-refine-architecture/references/health-check.md +1 -1
- package/src/skf-refine-architecture/references/improvements.md +9 -25
- package/src/skf-refine-architecture/references/init.md +9 -16
- package/src/skf-refine-architecture/references/issue-detection.md +10 -25
- package/src/skf-refine-architecture/references/refinement-rules.md +4 -16
- package/src/skf-refine-architecture/references/report.md +14 -11
- package/src/skf-rename-skill/SKILL.md +11 -21
- package/src/skf-rename-skill/customize.toml +10 -0
- package/src/skf-rename-skill/references/execute.md +112 -82
- package/src/skf-rename-skill/references/exit-codes.md +12 -0
- package/src/skf-rename-skill/references/report.md +16 -8
- package/src/skf-rename-skill/references/select.md +69 -42
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -0
- package/src/skf-setup/SKILL.md +21 -13
- package/src/skf-setup/customize.toml +10 -0
- package/src/skf-setup/references/auto-index.md +11 -10
- package/src/skf-setup/references/ccc-index.md +14 -15
- package/src/skf-setup/references/detect-and-tier.md +10 -8
- package/src/skf-setup/references/report.md +26 -32
- package/src/skf-setup/references/tier-rules.md +2 -32
- package/src/skf-setup/references/write-config.md +9 -10
- package/src/skf-test-skill/SKILL.md +10 -8
- package/src/skf-test-skill/customize.toml +2 -0
- package/src/skf-test-skill/references/coherence-check.md +19 -31
- package/src/skf-test-skill/references/coverage-check.md +111 -67
- package/src/skf-test-skill/references/detect-mode.md +10 -41
- package/src/skf-test-skill/references/external-validators.md +11 -19
- package/src/skf-test-skill/references/init.md +39 -21
- package/src/skf-test-skill/references/migration-section-rules.md +4 -4
- package/src/skf-test-skill/references/report.md +60 -40
- package/src/skf-test-skill/references/score.md +32 -64
- package/src/skf-test-skill/references/scoring-rules.md +6 -62
- package/src/skf-test-skill/references/source-access-protocol.md +15 -5
- package/src/skf-test-skill/references/step-hard-gate.md +1 -3
- package/src/skf-test-skill/scripts/aggregate-coherence.py +276 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +359 -0
- package/src/skf-test-skill/scripts/combine-external-scores.py +190 -0
- package/src/skf-test-skill/scripts/compute-score.py +135 -4
- package/src/skf-test-skill/scripts/reconcile-coverage.py +327 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +251 -0
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +192 -0
- package/src/skf-update-skill/SKILL.md +17 -5
- package/src/skf-update-skill/customize.toml +11 -6
- package/src/skf-update-skill/references/detect-changes.md +48 -36
- package/src/skf-update-skill/references/health-check.md +5 -11
- package/src/skf-update-skill/references/init.md +40 -32
- package/src/skf-update-skill/references/manual-section-rules.md +1 -2
- package/src/skf-update-skill/references/merge-conflict-rules.md +1 -52
- package/src/skf-update-skill/references/merge.md +13 -57
- package/src/skf-update-skill/references/re-extract.md +27 -25
- package/src/skf-update-skill/references/report.md +14 -7
- package/src/skf-update-skill/references/validate.md +32 -30
- package/src/skf-update-skill/references/write.md +86 -51
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +167 -0
- package/src/skf-verify-stack/SKILL.md +11 -21
- package/src/skf-verify-stack/customize.toml +12 -3
- package/src/skf-verify-stack/references/coverage-patterns.md +1 -29
- package/src/skf-verify-stack/references/coverage.md +21 -10
- package/src/skf-verify-stack/references/exit-codes.md +14 -0
- package/src/skf-verify-stack/references/health-check.md +1 -1
- package/src/skf-verify-stack/references/init.md +20 -25
- package/src/skf-verify-stack/references/integration-verification-rules.md +10 -19
- package/src/skf-verify-stack/references/integrations.md +44 -49
- package/src/skf-verify-stack/references/report.md +28 -34
- package/src/skf-verify-stack/references/requirements.md +3 -3
- package/src/skf-verify-stack/references/synthesize.md +36 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +196 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +256 -0
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +258 -0
- package/tools/cli/lib/ui.js +3 -2
- package/docs/deepwiki.md +0 -89
- package/src/skf-rename-skill/references/rebuild-context.md +0 -110
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
---
|
|
2
|
-
briefSchemaFile: 'assets/skill-brief-schema.md'
|
|
3
2
|
versionResolutionFile: 'references/version-resolution.md'
|
|
4
3
|
qmdRegistrationFile: 'references/qmd-collection-registration.md'
|
|
5
4
|
nextStepFile: 'health-check.md'
|
|
@@ -19,20 +18,15 @@ forgeTierFile: '{sidecar_path}/forge-tier.yaml'
|
|
|
19
18
|
|
|
20
19
|
# Step 5: Write Brief
|
|
21
20
|
|
|
22
|
-
## STEP GOAL:
|
|
23
|
-
|
|
24
|
-
To generate the complete skill-brief.yaml from the approved brief data and write it to the forge data folder, completing the brief-skill workflow.
|
|
25
|
-
|
|
26
21
|
## Rules
|
|
27
22
|
|
|
28
23
|
- Focus only on writing the file — all decisions have been made
|
|
29
24
|
- Do not change any field values without user request — the brief was already approved
|
|
30
|
-
- Create the output directory if it doesn't exist
|
|
31
25
|
- Chains to the local health-check step via `{nextStepFile}` after completion — the user-facing success summary is NOT the terminal step
|
|
32
26
|
- All user-facing output in `{communication_language}`; written artifact (`description`, `notes`) in `{document_output_language}`
|
|
33
27
|
- **Determinism delegation:** YAML rendering, version-precedence, atomic write, the headless result envelope, and the QMD-collection registry mutation are all delegated to shared SKF scripts. The LLM's job in this step is to assemble inputs, branch on script results, and surface user-facing prose — not to render YAML, JSON envelopes, or YAML-mutation diffs in the model.
|
|
34
28
|
|
|
35
|
-
##
|
|
29
|
+
## Sequence
|
|
36
30
|
|
|
37
31
|
### 1. Reference the Schema (LLM context only)
|
|
38
32
|
|
|
@@ -98,7 +92,7 @@ Assemble the brief context as a **flat** JSON object — every approved value is
|
|
|
98
92
|
"scope_rationale": null | {"recommended":"...","chosen":"...","accepted_recommendation":true|false,"heuristic":"...","reason":"...","recorded":"YYYY-MM-DD"},
|
|
99
93
|
"scope_tier_a_include": null | ["{tier-A authoring-surface patterns — from step 03 §3c capture, or hydrated on a ratify run}"],
|
|
100
94
|
"scope_amendments": null | [{"path":"...","action":"...","reason":"...","date":"YYYY-MM-DD","workflow":"..."}],
|
|
101
|
-
"doc_urls": null | [{"url": "...", "label": "..."}],
|
|
95
|
+
"doc_urls": null | [{"url": "...", "label": "...", "source": "{optional: language-registry|readme-detection|homepage|pages-api|docs-folder}"}],
|
|
102
96
|
"scripts_intent": null | "{detect|none|free-text}",
|
|
103
97
|
"assets_intent": null | "{detect|none|free-text}",
|
|
104
98
|
"source_authority": null | "{official|community|internal}",
|
|
@@ -163,7 +157,7 @@ The script derives `exit_code` deterministically from `halt_reason` (null→0, i
|
|
|
163
157
|
|
|
164
158
|
The script enforces the success/error halt_reason invariant (success requires null halt_reason; error requires non-null). The `user-cancelled` halt_reason is accepted for completeness (interactive `[X]` Cancel sites in step 1/03/04) but never appears on the headless code path.
|
|
165
159
|
|
|
166
|
-
Invocation sites (each pointed at this block, not duplicated): step 1 §1 (write-failed pre-resolution), step 1 §8 (input-missing/input-invalid GATE), step 5 §2b (overwrite-cancelled), step 5 §3 (input-invalid/write-failed from script).
|
|
160
|
+
Invocation sites (each pointed at this block, not duplicated): step 1 §1 (write-failed pre-resolution; forge-tier-missing), step 1 §8 (input-missing/input-invalid GATE), step 2 §1 (target-inaccessible/gh-auth-failed), step 5 §2b (overwrite-cancelled), step 5 §3 (input-invalid/write-failed from script). The step 1 §1 forge-tier-missing and step 2 §1 target-inaccessible/gh-auth-failed sites emit through this block too, so every headless HALT class surfaces a `SKF_BRIEF_RESULT_JSON` envelope — there are no envelope-silent failure classes.
|
|
167
161
|
|
|
168
162
|
When `{headless_mode}` is false, skip this section silently — no envelope is emitted.
|
|
169
163
|
|
|
@@ -203,10 +197,22 @@ After compilation, you can:
|
|
|
203
197
|
|
|
204
198
|
**Brief-skill workflow complete.**"
|
|
205
199
|
|
|
206
|
-
###
|
|
200
|
+
### 6b. On-Complete Hook (pipeline integration)
|
|
201
|
+
|
|
202
|
+
If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it now — after the brief has been written (§3) and the result contract finalized (§4b) — as:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
{onCompleteCommand} --result-path={brief_path}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
where `{brief_path}` is the absolute path captured from the §3 response envelope (the freshly written `skill-brief.yaml`, the stable artifact a downstream consumer chains from).
|
|
207
209
|
|
|
208
|
-
|
|
210
|
+
- On success: log to `workflow_warnings[]` as informational only if the hook emitted stderr (`on_complete hook stderr: …`); otherwise no entry.
|
|
211
|
+
- On non-zero exit / process error: log to `workflow_warnings[]` (`on_complete hook failed (exit {code}): {stderr_snippet}`).
|
|
212
|
+
- **Never fail the workflow on hook errors** — the hook is for pipeline integration (chaining into create-skill, Slack, dashboards, CI), not for gating brief production.
|
|
209
213
|
|
|
210
|
-
|
|
214
|
+
When `{onCompleteCommand}` is empty (bundled default), skip this section entirely — no hook is invoked.
|
|
215
|
+
|
|
216
|
+
### 7. Chain to Health Check
|
|
211
217
|
|
|
212
|
-
|
|
218
|
+
Once the brief file has been written and the success summary displayed, load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the summary reads as final.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: skf-campaign
|
|
3
|
+
description: Campaign orchestration — multi-library skill production with dependency tracking, file-based state, and resume. Use when the user asks to "run a campaign" or "orchestrate skills."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Campaign
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Orchestrates the production of 15+ skills across multiple sessions by driving them through the full SKF pipeline (brief, generate, compile, test, export) in dependency order. Campaign sits atop the pipeline ladder: it sequences the workflows that produce skills rather than producing artifacts itself. File-based state (`_campaign-state.yaml`) survives context death, enabling resume from any point.
|
|
11
|
+
|
|
12
|
+
## Conventions
|
|
13
|
+
|
|
14
|
+
- Bare paths (e.g. `references/step-01-setup.md`) resolve from the skill root.
|
|
15
|
+
- `references/` holds the stage-chained step files plus reference specs (e.g. the `_campaign-directive.md` contract at `references/campaign-directive-spec.md`); `templates/`, `scripts/`, and `assets/` hold templates, deterministic helpers, and the state schema.
|
|
16
|
+
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
|
|
17
|
+
- `{project-root}`-prefixed paths resolve from the project working directory.
|
|
18
|
+
- `{skill-name}` resolves to the skill directory's basename.
|
|
19
|
+
|
|
20
|
+
## Role
|
|
21
|
+
|
|
22
|
+
You are a campaign orchestrator operating in Ferris's Management mode. You sequence workflows, track per-skill state, enforce quality gates, and ensure every skill reaches its target tier — while the individual pipeline workflows handle the actual artifact production.
|
|
23
|
+
|
|
24
|
+
## On Activation
|
|
25
|
+
|
|
26
|
+
Run these steps once, in order, before dispatching to Mode Routing.
|
|
27
|
+
|
|
28
|
+
1. **Load config.** Read `{project-root}/_bmad/skf/config.yaml` and `{sidecar_path}/preferences.yaml` in one batched message (independent files). From config resolve `project_name`, `user_name`, `communication_language`, `document_output_language`, `skills_output_folder`, `forge_data_folder`, `sidecar_path`. From preferences resolve `headless_mode` (default false). If the config file is missing, fall back to `forge_data_folder = forge-data`.
|
|
29
|
+
|
|
30
|
+
2. **Resolve `{headless_mode}`** — true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `preferences.yaml`. Default: false.
|
|
31
|
+
|
|
32
|
+
3. **Resolve workflow customization.** Run:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The script merges three layers (scalars override, arrays append):
|
|
39
|
+
|
|
40
|
+
- `{skill-root}/customize.toml` — bundled defaults
|
|
41
|
+
- `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
|
|
42
|
+
- `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
|
|
43
|
+
|
|
44
|
+
If it fails or is missing, fall back to `{skill-root}/customize.toml` directly. Resolve each scalar now (so step files never repeat the conditional) and stash as workflow-context variables:
|
|
45
|
+
|
|
46
|
+
- `{campaignWorkspacePath}` ← `workflow.campaign_workspace_path` if non-empty, else `{forge_data_folder}/_campaign`
|
|
47
|
+
- `{qualityGateHard}` / `{qualityGateSoftTarget}` / `{qualityGateSoftFallback}` ← the `quality_gate_*` scalars (defaults `zero-critical-high` / `90` / `80`)
|
|
48
|
+
- `{reportTemplatePath}` ← `workflow.report_template_path` if non-empty, else `templates/campaign-report-template.md`
|
|
49
|
+
- `{kickoffTemplatePath}` ← `workflow.kickoff_template_path` if non-empty, else `templates/kickoff-template.md`
|
|
50
|
+
- `{briefTemplatePath}` ← `workflow.brief_template_path` if non-empty, else `templates/campaign-brief-template.yaml`
|
|
51
|
+
- `{onComplete}` ← `workflow.on_complete` (empty = no-op)
|
|
52
|
+
|
|
53
|
+
Load `workflow.persistent_facts` (literal sentences and `file:` references, globs expanded) and keep them in mind for the whole campaign — they are injected into every per-skill kickoff. Run any `activation_steps_prepend` before step 1 and any `activation_steps_append` after this step.
|
|
54
|
+
|
|
55
|
+
4. **Parse CLI overrides** into the workflow context:
|
|
56
|
+
|
|
57
|
+
| Flag | Effect |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `--headless` / `-H` | Force `{headless_mode} = true` (see step 2). |
|
|
60
|
+
| `--brief <file>` | Seed step-01 targets from a `campaign-brief.yaml` instead of interactive prompts. Implies `--headless`. |
|
|
61
|
+
| `--manifest <file>` | Seed step-01 targets from a plain-text `name,repo_url,tier,pin` manifest. Implies `--headless`. |
|
|
62
|
+
| `--from <skill>` | Resume override — see Mode Routing. |
|
|
63
|
+
|
|
64
|
+
If `--brief` or `--manifest` is set, force `{headless_mode} = true` (log "headless: coerced by --brief/--manifest" if it was false).
|
|
65
|
+
|
|
66
|
+
**`--manifest` format:** one `name,repo_url,tier,pin` target per line (empty `pin` = latest); a trailing `;dep1,dep2` segment sets `depends_on`; blank and `#` lines are skipped. A malformed line HALTs step-01 with the offending line numbers — never a partial target set.
|
|
67
|
+
|
|
68
|
+
5. **Dispatch** per Mode Routing below.
|
|
69
|
+
|
|
70
|
+
## Workflow Rules
|
|
71
|
+
|
|
72
|
+
These rules apply to every step in this workflow:
|
|
73
|
+
|
|
74
|
+
- State-first — write state to disk before chaining to the next step or workflow
|
|
75
|
+
- Read-backup-modify-write for all state mutations (State Contract in `references/campaign-contracts.md`)
|
|
76
|
+
- Validate `_campaign-state.yaml` on every load by running `uv run scripts/campaign-validate-state.py --state-file {stateFile}` and HALT (exit code 3, `invalid-state`) on non-zero — never hand-validate the schema
|
|
77
|
+
- Zero memory dependency — campaign state is 100% recoverable from disk; never rely on conversation context for progress tracking
|
|
78
|
+
- Treat a missing or unparseable `SKF_*_RESULT_JSON` envelope from any sub-skill as a sub-skill failure; never write partial state from an unparsed envelope
|
|
79
|
+
- Append a one-line entry to the campaign decision log (`{campaignWorkspacePath}/_campaign-decision-log.md`, append-only) at every operator or auto-decision (skip/force, overwrite, export cancel/proceed, `.bak` recovery, user-cancel) so rationale survives compaction and resume
|
|
80
|
+
- **Universal cancel affordance** — at any interactive gate between Setup and the Export gate, `cancel`/`exit`/`:q` triggers a HARD HALT with **exit code 12 (`user-cancelled`)**: log it and leave state intact and resumable. Exception: the Export gate's own `[C]ancel` stays exit code 11 (`export-cancelled`) — never also emit 12 there, so an automator's exit-code branch stays deterministic. These keywords count only as a response *to a prompt*; a skill or campaign named `cancel`/`exit` supplied as data is never treated as a cancel.
|
|
81
|
+
- Always communicate in `{communication_language}`
|
|
82
|
+
- If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
|
|
83
|
+
- If `{headless_mode}` is true, emit a single-line JSON progress event to **stderr** at each step's entry, exit, and HARD HALT so schedulers stream live progress — event format in `references/campaign-contracts.md` (Headless Progress Events)
|
|
84
|
+
|
|
85
|
+
## Stages
|
|
86
|
+
|
|
87
|
+
| # | Step | File | Auto-proceed |
|
|
88
|
+
|---|------|------|--------------|
|
|
89
|
+
| 0 | Setup | references/step-01-setup.md | Yes |
|
|
90
|
+
| 1 | Strategy | references/step-02-strategy.md | Yes |
|
|
91
|
+
| 2 | Pin Validation | references/step-03-pins.md | Yes |
|
|
92
|
+
| 3 | Provenance | references/step-04-provenance.md | Yes |
|
|
93
|
+
| 4 | Skill Loop | references/step-05-skill-loop.md | Yes |
|
|
94
|
+
| 5 | Tier B Batch | references/step-06-batch.md | Yes |
|
|
95
|
+
| 6 | Capstone | references/step-07-capstone.md | Yes |
|
|
96
|
+
| 7 | Verification | references/step-08-verify.md | Yes |
|
|
97
|
+
| 8 | Refinement | references/step-09-refine.md | Yes |
|
|
98
|
+
| 9 | Export | references/step-10-export.md | No (write-gate HALT) |
|
|
99
|
+
| 10 | Maintenance | references/step-11-maintenance.md | Yes |
|
|
100
|
+
|
|
101
|
+
**Stage numbering:** step files are 1-indexed (`step-01` … `step-11`); `campaign.current_stage` in state is 0-indexed, so step-`NN` runs stage `NN − 1` (step-01 = stage 0, step-11 = stage 10). `references/step-resume.md` §3–§4 own how a resolved stage maps back to its step file on resume (including the `current_stage + 1` advance when no skill is active).
|
|
102
|
+
|
|
103
|
+
## Invocation Contract
|
|
104
|
+
|
|
105
|
+
| Aspect | Detail |
|
|
106
|
+
|--------|--------|
|
|
107
|
+
| **Inputs** | `campaign` to start a new campaign; `campaign resume [--from=<skill>]` to resume from last active or specified skill; `campaign status` for a read-only progress summary |
|
|
108
|
+
| **Outputs** | `_campaign-state.yaml` (state), `campaign-brief.yaml` (machine-generated brief), `campaign-report.md` (post-campaign summary), `_campaign-decision-log.md` (append-only rationale), `SKF_CAMPAIGN_RESULT_JSON` (headless envelope) — all under `{campaignWorkspacePath}` |
|
|
109
|
+
|
|
110
|
+
## Contracts
|
|
111
|
+
|
|
112
|
+
Exit codes, the HARD-HALT error envelope, the read-backup-modify-write **State Contract**, the headless success envelope, and per-step progress events live in `references/campaign-contracts.md` — consult it when you HALT, mutate state, or emit headless output.
|
|
113
|
+
|
|
114
|
+
## Mode Routing
|
|
115
|
+
|
|
116
|
+
On invocation:
|
|
117
|
+
|
|
118
|
+
1. **`campaign resume [--from=<skill>]`** — load `references/step-resume.md` (validates state, recovers from backup, chains to the right stage). `--from=<skill>` overrides the resume point to the named skill.
|
|
119
|
+
2. **`campaign`** (new, no existing state) — run from stage 0 (Setup).
|
|
120
|
+
3. **`campaign`** (state exists) — detect existing `{campaignWorkspacePath}/_campaign-state.yaml` and prompt **resume** (via `references/step-resume.md`) or **overwrite**. On overwrite, first archive the existing `_campaign-state.yaml` and `campaign-brief.yaml` to `{campaignWorkspacePath}/archive/{name}-{timestamp}/` and log it before chaining to step-01. In headless mode, default to **resume** (never silently clobber); archive-and-overwrite only when `--brief`/`--manifest` explicitly seeds a new campaign.
|
|
121
|
+
4. **`campaign status`** (read-only) — load `{campaignWorkspacePath}/_campaign-state.yaml`, validate it via `campaign-validate-state.py`, then run `uv run scripts/campaign-status.py --state-file {campaignWorkspacePath}/_campaign-state.yaml` and display its summary (campaign name, current stage, completed-vs-total, per-status counts) followed by the last ~15 lines of `{campaignWorkspacePath}/_campaign-decision-log.md` for the recent decision trail, then stop. No backup, no mutation, no chaining. Exit 0 (or 9 if the state is unrecoverable).
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "campaign-state",
|
|
4
|
+
"description": "Schema for _campaign-state.yaml — single source of truth for campaign orchestration state",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["campaign", "skills", "dependency_graph"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"campaign": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["name", "started_at", "last_updated", "current_stage", "quality_gate", "health_findings_queue"],
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"name": {
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
"started_at": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"format": "date-time"
|
|
20
|
+
},
|
|
21
|
+
"last_updated": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"format": "date-time"
|
|
24
|
+
},
|
|
25
|
+
"current_stage": {
|
|
26
|
+
"type": "integer",
|
|
27
|
+
"minimum": 0,
|
|
28
|
+
"maximum": 10
|
|
29
|
+
},
|
|
30
|
+
"directive_path": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
"quality_gate": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": ["hard", "soft_target", "soft_fallback"],
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"properties": {
|
|
38
|
+
"hard": {
|
|
39
|
+
"type": "string"
|
|
40
|
+
},
|
|
41
|
+
"soft_target": {
|
|
42
|
+
"type": "number"
|
|
43
|
+
},
|
|
44
|
+
"soft_fallback": {
|
|
45
|
+
"type": "number"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"health_findings_queue": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"enum": ["local", "improvement"]
|
|
52
|
+
},
|
|
53
|
+
"architecture_doc_path": {
|
|
54
|
+
"type": ["string", "null"],
|
|
55
|
+
"description": "Path to the architecture document consumed by the verify (Stage 7) and refine (Stage 8) stages. Sourced at setup; null when the campaign runs no verification."
|
|
56
|
+
},
|
|
57
|
+
"capstone": {
|
|
58
|
+
"type": ["object", "null"],
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"description": "Capstone (stack) outcome composed from all completed campaign skills (Tier A and Tier B). Campaign-level summary; the composed skill itself lives at skill_path.",
|
|
61
|
+
"properties": {
|
|
62
|
+
"skill_path": {
|
|
63
|
+
"type": ["string", "null"]
|
|
64
|
+
},
|
|
65
|
+
"quality_score": {
|
|
66
|
+
"type": ["number", "null"]
|
|
67
|
+
},
|
|
68
|
+
"verified": {
|
|
69
|
+
"type": ["boolean", "null"]
|
|
70
|
+
},
|
|
71
|
+
"completed_at": {
|
|
72
|
+
"type": ["string", "null"],
|
|
73
|
+
"format": "date-time"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"verification": {
|
|
78
|
+
"type": ["object", "null"],
|
|
79
|
+
"additionalProperties": false,
|
|
80
|
+
"description": "Stage 7 verify-stack summary. Detailed findings live in the external report at report_path.",
|
|
81
|
+
"properties": {
|
|
82
|
+
"report_path": {
|
|
83
|
+
"type": ["string", "null"]
|
|
84
|
+
},
|
|
85
|
+
"overall_verdict": {
|
|
86
|
+
"type": ["string", "null"],
|
|
87
|
+
"enum": ["Verified", "Plausible", "Risky", "Blocked", null]
|
|
88
|
+
},
|
|
89
|
+
"coverage_percentage": {
|
|
90
|
+
"type": ["number", "null"]
|
|
91
|
+
},
|
|
92
|
+
"recommendation_count": {
|
|
93
|
+
"type": ["integer", "null"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"refinement": {
|
|
98
|
+
"type": ["object", "null"],
|
|
99
|
+
"additionalProperties": false,
|
|
100
|
+
"description": "Stage 8 refine-architecture summary. The refined document lives at refined_path.",
|
|
101
|
+
"properties": {
|
|
102
|
+
"refined_path": {
|
|
103
|
+
"type": ["string", "null"]
|
|
104
|
+
},
|
|
105
|
+
"gap_count": {
|
|
106
|
+
"type": ["integer", "null"]
|
|
107
|
+
},
|
|
108
|
+
"issue_count": {
|
|
109
|
+
"type": ["integer", "null"]
|
|
110
|
+
},
|
|
111
|
+
"improvement_count": {
|
|
112
|
+
"type": ["integer", "null"]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"skills": {
|
|
119
|
+
"type": "array",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": "object",
|
|
122
|
+
"required": ["name", "status", "tier"],
|
|
123
|
+
"additionalProperties": false,
|
|
124
|
+
"properties": {
|
|
125
|
+
"name": {
|
|
126
|
+
"type": "string"
|
|
127
|
+
},
|
|
128
|
+
"status": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"enum": ["pending", "active", "completed", "failed", "skipped"]
|
|
131
|
+
},
|
|
132
|
+
"depends_on": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
"items": {
|
|
135
|
+
"type": "string"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"tier": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"enum": ["A", "B"]
|
|
141
|
+
},
|
|
142
|
+
"pin": {
|
|
143
|
+
"type": ["string", "null"]
|
|
144
|
+
},
|
|
145
|
+
"brief_path": {
|
|
146
|
+
"type": ["string", "null"]
|
|
147
|
+
},
|
|
148
|
+
"skill_path": {
|
|
149
|
+
"type": ["string", "null"]
|
|
150
|
+
},
|
|
151
|
+
"quality_score": {
|
|
152
|
+
"type": ["number", "null"]
|
|
153
|
+
},
|
|
154
|
+
"workarounds_applied": {
|
|
155
|
+
"type": "array",
|
|
156
|
+
"items": {
|
|
157
|
+
"type": "string"
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"started_at": {
|
|
161
|
+
"type": ["string", "null"],
|
|
162
|
+
"format": "date-time"
|
|
163
|
+
},
|
|
164
|
+
"completed_at": {
|
|
165
|
+
"type": ["string", "null"],
|
|
166
|
+
"format": "date-time"
|
|
167
|
+
},
|
|
168
|
+
"commit_sha": {
|
|
169
|
+
"type": ["string", "null"]
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"dependency_graph": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"required": ["execution_order", "circular_deps_detected"],
|
|
177
|
+
"additionalProperties": false,
|
|
178
|
+
"properties": {
|
|
179
|
+
"execution_order": {
|
|
180
|
+
"type": "array",
|
|
181
|
+
"items": {
|
|
182
|
+
"type": "string"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"circular_deps_detected": {
|
|
186
|
+
"type": "boolean"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# DO NOT EDIT -- overwritten on every update.
|
|
2
|
+
#
|
|
3
|
+
# Workflow customization surface for skf-campaign.
|
|
4
|
+
# Team overrides: _bmad/custom/skf-campaign.toml (under {project-root})
|
|
5
|
+
# Personal overrides: _bmad/custom/skf-campaign.user.toml (under {project-root})
|
|
6
|
+
|
|
7
|
+
[workflow]
|
|
8
|
+
|
|
9
|
+
# --- Configurable below. Overrides merge per BMad structural rules: ---
|
|
10
|
+
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
|
|
11
|
+
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
|
|
12
|
+
|
|
13
|
+
# Steps to run before the standard activation (config load, customization
|
|
14
|
+
# resolve). Overrides append. Use for org-wide pre-flight checks (auth,
|
|
15
|
+
# network, compliance) that must precede any orchestration work.
|
|
16
|
+
|
|
17
|
+
activation_steps_prepend = []
|
|
18
|
+
|
|
19
|
+
# Steps to run after activation but before the first stage executes.
|
|
20
|
+
# Overrides append. Use for context loads or banner customization that
|
|
21
|
+
# should run once activation completes successfully.
|
|
22
|
+
|
|
23
|
+
activation_steps_append = []
|
|
24
|
+
|
|
25
|
+
# Persistent facts the workflow keeps in mind for the whole campaign
|
|
26
|
+
# (house style, naming conventions, quality guardrails). These are injected
|
|
27
|
+
# into every per-skill kickoff, so project-scoped house facts propagate to
|
|
28
|
+
# the whole campaign. Overrides append.
|
|
29
|
+
#
|
|
30
|
+
# Each entry is either:
|
|
31
|
+
# - a literal sentence, e.g. "All skills must cite their upstream source."
|
|
32
|
+
# - a file reference prefixed with `file:`, e.g.
|
|
33
|
+
# "file:{project-root}/docs/skill-style.md" (globs supported; file
|
|
34
|
+
# contents are loaded and treated as facts).
|
|
35
|
+
|
|
36
|
+
persistent_facts = [
|
|
37
|
+
"file:{project-root}/**/project-context.md",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# --- Workspace path ---
|
|
41
|
+
#
|
|
42
|
+
# Where the campaign keeps its state, backup, brief, batch input, archive,
|
|
43
|
+
# and decision log. Empty string = use the bundled default
|
|
44
|
+
# `{forge_data_folder}/_campaign`. Override to relocate campaign artifacts
|
|
45
|
+
# (e.g. a shared volume) without editing every step file. Resolved once in
|
|
46
|
+
# On Activation as {campaignWorkspacePath}.
|
|
47
|
+
|
|
48
|
+
campaign_workspace_path = ""
|
|
49
|
+
|
|
50
|
+
# --- Quality gate ---
|
|
51
|
+
#
|
|
52
|
+
# Campaign-wide quality bar applied to every skill. Scalars override the
|
|
53
|
+
# bundled defaults; the per-campaign brief and any directive `## Quality
|
|
54
|
+
# Overrides` still take precedence at runtime. Override here to change the
|
|
55
|
+
# org-wide default without forking the skill.
|
|
56
|
+
|
|
57
|
+
quality_gate_hard = "zero-critical-high"
|
|
58
|
+
quality_gate_soft_target = 90
|
|
59
|
+
quality_gate_soft_fallback = 80
|
|
60
|
+
|
|
61
|
+
# --- Optional template overrides ---
|
|
62
|
+
#
|
|
63
|
+
# Lift the canonical template paths so orgs can substitute house-style copies
|
|
64
|
+
# without forking the skill. Empty string = use the bundled default under
|
|
65
|
+
# `templates/`.
|
|
66
|
+
|
|
67
|
+
report_template_path = ""
|
|
68
|
+
kickoff_template_path = ""
|
|
69
|
+
brief_template_path = ""
|
|
70
|
+
|
|
71
|
+
# Optional post-completion hook. When non-empty, the workflow invokes:
|
|
72
|
+
# <on_complete> --report-path=<path-to-campaign-report.md>
|
|
73
|
+
# after the campaign report is finalized (step-11). Failures are logged to
|
|
74
|
+
# the decision log but never fail the campaign. Empty = no-op.
|
|
75
|
+
on_complete = ""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
code: CA
|
|
2
|
+
name: skf-campaign
|
|
3
|
+
description: "Campaign orchestration — multi-library skill production with dependency tracking"
|
|
4
|
+
version: "2.0.0"
|
|
5
|
+
trigger: campaign
|
|
6
|
+
parent_module: skf
|
|
7
|
+
|
|
8
|
+
# Canonical internal filenames for the campaign workspace. Fixed contract (the
|
|
9
|
+
# step chain references these names directly and test-skf-campaign-state guards
|
|
10
|
+
# them against drift) — NOT an operator-override surface; customize.toml is the
|
|
11
|
+
# only customization mechanism.
|
|
12
|
+
config:
|
|
13
|
+
state_file: "_campaign-state.yaml"
|
|
14
|
+
backup_file: "_campaign-state.yaml.bak"
|
|
15
|
+
directive_file: "_campaign-directive.md"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
2
|
+
|
|
3
|
+
# Campaign Contracts
|
|
4
|
+
|
|
5
|
+
The self-contained contracts consulted at a specific moment — when a step HALTs, mutates state, or emits the final headless envelope. Each stands alone so it survives context compaction.
|
|
6
|
+
|
|
7
|
+
## Exit Codes
|
|
8
|
+
|
|
9
|
+
Every HARD HALT exits with a stable, documented code so headless automators can branch on the failure class without grepping message text:
|
|
10
|
+
|
|
11
|
+
| Code | Meaning | Raised by |
|
|
12
|
+
| ---- | -------------------- | ----------------------------------------------------------- |
|
|
13
|
+
| 0 | success | step-11 (terminal); step-resume §3–§4 (campaign already complete — nothing to resume) |
|
|
14
|
+
| 2 | invalid-input | step-01 §1 (no targets, or a malformed `--manifest` line); steps 02/03/04/06 §4 (a helper reports unreadable input or a required tool such as `gh` unavailable); step-resume §1/§3 (resume targets a missing campaign or unknown skill) |
|
|
15
|
+
| 3 | invalid-state | any step §1 (`campaign-validate-state.py` non-zero on load) |
|
|
16
|
+
| 4 | circular-deps | step-02 §5 (a dependency cycle, or a dangling `depends_on` reference — either way the graph cannot be ordered) |
|
|
17
|
+
| 5 | invalid-pin | step-03 §5 |
|
|
18
|
+
| 6 | inaccessible-repo | step-04 §5 |
|
|
19
|
+
| 7 | dependency-deadlock | step-05 §4 (no skill ready and no recovery chosen) |
|
|
20
|
+
| 8 | missing-brief | step-03/04/05 §2 and step-06 §4 (brief missing/unreadable, or a Tier B target has no matching brief entry) |
|
|
21
|
+
| 9 | corrupt-state | step-resume §1 (primary unrecoverable, `.bak` also invalid) |
|
|
22
|
+
| 10 | report-failure | step-11 §2 — **degraded only**: the report could not be generated; the campaign still completes and state stays intact (never a hard halt that discards a finished campaign) |
|
|
23
|
+
| 11 | export-cancelled | step-10 §4 (operator chose `[C]ancel` — graceful, resumable) |
|
|
24
|
+
| 12 | user-cancelled | any interactive gate (operator typed `cancel` / `exit` / `:q` at a prompt between Setup and the Export gate — graceful, resumable) |
|
|
25
|
+
|
|
26
|
+
## Result Contract on HARD HALT
|
|
27
|
+
|
|
28
|
+
In addition to the success-variant envelope (see Campaign Headless Envelope below), every HARD HALT emits an **error variant** so automators don't silently break. Emit one line on **stderr**:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
SKF_CAMPAIGN_RESULT_JSON: {"status":"error","exit_code":<N>,"phase":"<slug>","error":{"code":"<class>","message":"<short>"},"skills_completed":N,"skills_failed":N,"campaign_report_path":null,"decision_log":"<path-or-null>"}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`<class>` is the Exit Codes meaning (e.g. `circular-deps`, `inaccessible-repo`); `<slug>` is the step where the HALT occurred. One line, no pretty-print.
|
|
35
|
+
|
|
36
|
+
## State Contract
|
|
37
|
+
|
|
38
|
+
All state mutations follow the read-backup-modify-write pattern:
|
|
39
|
+
|
|
40
|
+
1. **Read** `_campaign-state.yaml`
|
|
41
|
+
2. **Validate** via `uv run scripts/campaign-validate-state.py --state-file {stateFile}` (halt on invalid)
|
|
42
|
+
3. **Backup** — copy current `_campaign-state.yaml` to `_campaign-state.yaml.bak`
|
|
43
|
+
4. **Modify** in memory
|
|
44
|
+
5. **Update** `campaign.last_updated` to current ISO-8601 timestamp
|
|
45
|
+
6. **Write** modified state back to `_campaign-state.yaml`
|
|
46
|
+
|
|
47
|
+
The `.bak` file is one-deep (overwritten on every write). If the primary file is corrupted (crash during write), the `.bak` file contains the last valid state — step-resume §1 recovers from it automatically rather than dead-halting.
|
|
48
|
+
|
|
49
|
+
## Campaign Headless Envelope
|
|
50
|
+
|
|
51
|
+
When `{headless_mode}` is true, the final step emits a single-line JSON envelope on stdout:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
SKF_CAMPAIGN_RESULT_JSON: {"status":"success|error","skills_completed":0,"skills_failed":0,"quality_scores":{},"campaign_report_path":"","decision_log":"","duration":""}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`status` is `"success"` when the campaign completes normally, `"error"` on any unrecoverable halt (with `exit_code` per the Exit Codes table — see Result Contract on HARD HALT above). `skills_completed` and `skills_failed` count per-skill outcomes. `quality_scores` maps skill names to their test-skill scores. `campaign_report_path` points to the generated `campaign-report.md`. `decision_log` points to `_campaign-decision-log.md`. `duration` is the wall-clock time of the campaign run. Populate the counts, `quality_scores`, and `duration` directly from the `campaign-report.py` result JSON (step-11 §2) — do not recompute them by hand.
|
|
58
|
+
|
|
59
|
+
## Headless Progress Events
|
|
60
|
+
|
|
61
|
+
When `{headless_mode}` is true, emit a single-line JSON progress event to **stderr** at each step's entry, exit, and HARD HALT, so schedulers stream live progress instead of post-mortem-parsing the final envelope:
|
|
62
|
+
|
|
63
|
+
- entry: `{"stage":N,"name":"<slug>","status":"start"}`
|
|
64
|
+
- exit (just before chaining): `{"stage":N,"name":"<slug>","status":"done"}`
|
|
65
|
+
- on HARD HALT: `{"stage":N,"name":"<slug>","status":"halt","exit":<code>}` instead of `"done"`
|
|
66
|
+
|
|
67
|
+
`N` is the 0-indexed stage number (0–10) and `<slug>` is the kebab portion of the step filename. For the non-numbered routing/terminal steps (`resume`, `health-check`) emit `"stage":null` with the slug. One line per event; do not pretty-print.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
|
|
3
|
+
directiveFile: '_campaign-directive.md'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Campaign Directive Specification
|
|
7
|
+
|
|
8
|
+
Canonical contract for the campaign directive (`_campaign-directive.md`) — a
|
|
9
|
+
file-based standing directive holding campaign-wide policy. When a step loads
|
|
10
|
+
`campaign.directive_path` it re-reads the file fresh from disk at stage entry
|
|
11
|
+
(no caching, so operator edits between stages are picked up) and applies the
|
|
12
|
+
sections below as campaign-wide context. UTF-8 markdown; frontmatter not
|
|
13
|
+
required; default filename `_campaign-directive.md`.
|
|
14
|
+
|
|
15
|
+
## Recognized Sections
|
|
16
|
+
|
|
17
|
+
All optional — the directive may hold any combination of these, or none:
|
|
18
|
+
|
|
19
|
+
- **`## Quality Overrides`** — operator adjustments to quality gates for specific skills or the whole campaign.
|
|
20
|
+
- **`## Skip List`** — skills to skip during processing, with rationale.
|
|
21
|
+
- **`## Pipeline Flags`** — per-skill or campaign-wide pipeline modifiers.
|
|
22
|
+
- **`## Notes`** — free-form operator context for the agent processing the campaign.
|
|
23
|
+
|
|
24
|
+
Any heading not listed above is treated as general guidance: read it and apply
|
|
25
|
+
judgment based on the content. This lets operators add ad-hoc context without
|
|
26
|
+
modifying this specification.
|
|
27
|
+
|
|
28
|
+
## Absence Behavior
|
|
29
|
+
|
|
30
|
+
- If `campaign.directive_path` is not set in the state file: no error, proceed with defaults
|
|
31
|
+
- If `campaign.directive_path` is set but the file does not exist at that path: no error, proceed with defaults
|
|
32
|
+
- The directive is always optional — a campaign runs identically without one
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Note: `shared/health-check.md` resolves relative to the SKF module root
|
|
3
|
+
# ({project-root}/_bmad/skf/ when installed, {project-root}/src/ during
|
|
4
|
+
# development), NOT relative to this step file.
|
|
5
|
+
nextStepFile: 'shared/health-check.md'
|
|
6
|
+
stateFile: '{campaignWorkspacePath}/_campaign-state.yaml'
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
10
|
+
|
|
11
|
+
# Workflow Health Check
|
|
12
|
+
|
|
13
|
+
## STEP GOAL:
|
|
14
|
+
|
|
15
|
+
Apply the operator's findings-routing consent, then chain to the shared workflow self-improvement health check at `{nextStepFile}`. This is the terminal step of skf-campaign — after the shared health check completes, the workflow is fully done.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
19
|
+
- No user-facing reports, file writes, or result contracts in this step — those belong in step 11.
|
|
20
|
+
- The ONLY processing permitted here is reading the routing preference (below) and carrying it into `{nextStepFile}`; otherwise delegate directly with no additional commentary.
|
|
21
|
+
|
|
22
|
+
## MANDATORY SEQUENCE
|
|
23
|
+
|
|
24
|
+
### 1. Apply findings-routing consent
|
|
25
|
+
|
|
26
|
+
Read `campaign.health_findings_queue` from `{stateFile}` and carry it into the shared health check as the operator's **already-decided** routing consent (so the shared step does not re-prompt, satisfying its "explicit opt-in" requirement for non-bug findings):
|
|
27
|
+
|
|
28
|
+
- `"improvement"` — the operator opted in at setup: route this campaign's friction/gap findings to the shared improvement queue (the shared step's opt-in is pre-satisfied; do not re-prompt).
|
|
29
|
+
- `"local"` (default) — keep findings in the local queue only; do not submit to the shared queue.
|
|
30
|
+
|
|
31
|
+
If the state file is unreadable, default to `"local"` (never submit without consent).
|
|
32
|
+
|
|
33
|
+
### 2. Delegate
|
|
34
|
+
|
|
35
|
+
Load `{nextStepFile}`, read it fully, then execute it — applying the routing consent from step 1.
|