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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.10"
|
|
4
|
+
# ///
|
|
5
|
+
"""Deterministic doc-rot correction-indicator scan (step-doc-rot.md §2).
|
|
6
|
+
|
|
7
|
+
step 5c self-declares its correction scan "grep-based and deterministic — no AI
|
|
8
|
+
judgment is used for detection." This helper *is* that grep: it walks the
|
|
9
|
+
resolved feeder artifacts, matches every line against the fixed 13-row
|
|
10
|
+
correction-pattern table with case-insensitive substring containment (no regex,
|
|
11
|
+
no semantics), and emits the matches as JSON. It applies the one positional
|
|
12
|
+
filter the step documents — dropping matches that land inside the compiled
|
|
13
|
+
SKILL.md's own `## Migration & Deprecation Warnings` section (compile §4b
|
|
14
|
+
already authored those, so re-emitting them would be circular) — before it
|
|
15
|
+
returns. Running the scan here (instead of in-prompt) makes the "deterministic,
|
|
16
|
+
identical input → identical output" promise actually hold: the model no longer
|
|
17
|
+
hand-greps multi-KB artifacts.
|
|
18
|
+
|
|
19
|
+
The genuine judgment the step keeps in-prompt is untouched by this script:
|
|
20
|
+
enriching each match's `affected` symbol from surrounding context (§2) and
|
|
21
|
+
choosing where the `## CORRECTION` block goes (§3). The script emits every
|
|
22
|
+
deterministic field (`source`, `pattern`, `category`, `context_line`,
|
|
23
|
+
`line_number`); the prompt adds `affected`.
|
|
24
|
+
|
|
25
|
+
The pattern table and category labels are the contract in step-doc-rot.md §2 —
|
|
26
|
+
keep the two in lockstep.
|
|
27
|
+
|
|
28
|
+
CLI usage:
|
|
29
|
+
uv run scan-doc-rot.py --skill-md <staged SKILL.md> [FEEDER ...]
|
|
30
|
+
uv run scan-doc-rot.py --feeder evidence-report.md --feeder provenance-map.json
|
|
31
|
+
|
|
32
|
+
--skill-md the compiled/staged SKILL.md feeder (feeder #4); matches inside its
|
|
33
|
+
`## Migration & Deprecation Warnings` section are excluded. It is
|
|
34
|
+
also scanned like any other feeder.
|
|
35
|
+
FEEDER any other feeder artifact (evidence-report.md, provenance-map.json,
|
|
36
|
+
temporal-context files). Repeatable positionally or via --feeder.
|
|
37
|
+
|
|
38
|
+
Missing or empty files are skipped silently (not an error), matching §1's
|
|
39
|
+
"attempt to load; if it does not exist or is empty, skip it."
|
|
40
|
+
|
|
41
|
+
Output (stdout, one object):
|
|
42
|
+
{
|
|
43
|
+
"scanned": ["<path>", ...], # feeders that existed and were non-empty
|
|
44
|
+
"matches": [ # correction_matches[] (affected added in-prompt)
|
|
45
|
+
{"source": "<path>", "pattern": "deprecated", "category": "Deprecation",
|
|
46
|
+
"context_line": "<line text>", "line_number": <1-indexed int>},
|
|
47
|
+
...
|
|
48
|
+
],
|
|
49
|
+
"match_count": <int>,
|
|
50
|
+
"excluded_count": <int> # SKILL.md matches dropped by the §4b filter
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Exit codes:
|
|
54
|
+
0 — scan emitted successfully (including the zero-match case)
|
|
55
|
+
1 — invalid arguments (no feeders supplied)
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
from __future__ import annotations
|
|
59
|
+
|
|
60
|
+
import argparse
|
|
61
|
+
import json
|
|
62
|
+
import re
|
|
63
|
+
import sys
|
|
64
|
+
from pathlib import Path
|
|
65
|
+
|
|
66
|
+
# Fixed correction-pattern table — mirrors step-doc-rot.md §2. (pattern, category)
|
|
67
|
+
# Order is the table order; scanning is per-pattern so overlapping patterns
|
|
68
|
+
# (e.g. "deprecated" ⊂ "@deprecated") each record their own hit, exactly as the
|
|
69
|
+
# 13-row table enumerates them.
|
|
70
|
+
PATTERN_TABLE: list[tuple[str, str]] = [
|
|
71
|
+
("deprecated", "Deprecation"),
|
|
72
|
+
("@deprecated", "Deprecation"),
|
|
73
|
+
("breaking change", "Breaking change"),
|
|
74
|
+
("BREAKING", "Breaking change"),
|
|
75
|
+
("removed in", "Removal"),
|
|
76
|
+
("was removed", "Removal"),
|
|
77
|
+
("renamed to", "Rename"),
|
|
78
|
+
("renamed from", "Rename"),
|
|
79
|
+
("superseded by", "Supersession"),
|
|
80
|
+
("replaced by", "Supersession"),
|
|
81
|
+
("no longer supported", "End of life"),
|
|
82
|
+
("migration required", "Migration"),
|
|
83
|
+
("signature changed", "Signature change"),
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
_MIGRATION_HEADING = re.compile(r"^\s*##\s+Migration\s*&\s*Deprecation Warnings", re.IGNORECASE)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def scan_text(text: str, source: str) -> list[dict]:
|
|
90
|
+
"""Case-insensitive substring scan of `text` against PATTERN_TABLE.
|
|
91
|
+
|
|
92
|
+
Returns one record per (line, pattern) hit, in file order then table order.
|
|
93
|
+
line_number is 1-indexed. Pure — no I/O.
|
|
94
|
+
"""
|
|
95
|
+
matches: list[dict] = []
|
|
96
|
+
for idx, line in enumerate(text.splitlines(), start=1):
|
|
97
|
+
lowered = line.lower()
|
|
98
|
+
for pattern, category in PATTERN_TABLE:
|
|
99
|
+
if pattern.lower() in lowered:
|
|
100
|
+
matches.append(
|
|
101
|
+
{
|
|
102
|
+
"source": source,
|
|
103
|
+
"pattern": pattern,
|
|
104
|
+
"category": category,
|
|
105
|
+
"context_line": line.strip(),
|
|
106
|
+
"line_number": idx,
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
return matches
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def migration_section_range(skill_md_text: str) -> tuple[int, int] | None:
|
|
113
|
+
"""1-indexed [start, end) line window of the `## Migration & Deprecation
|
|
114
|
+
Warnings` section, or None if the section is absent.
|
|
115
|
+
|
|
116
|
+
start = the heading line; end = the next level-2 (`## `) heading, or EOF.
|
|
117
|
+
A `### ` subsection inside the section does NOT close it (only a sibling
|
|
118
|
+
`## ` heading does), matching §2's "before the next `##` heading".
|
|
119
|
+
"""
|
|
120
|
+
lines = skill_md_text.splitlines()
|
|
121
|
+
start = None
|
|
122
|
+
for idx, line in enumerate(lines, start=1):
|
|
123
|
+
if _MIGRATION_HEADING.match(line):
|
|
124
|
+
start = idx
|
|
125
|
+
break
|
|
126
|
+
if start is None:
|
|
127
|
+
return None
|
|
128
|
+
end = len(lines) + 1
|
|
129
|
+
for idx in range(start + 1, len(lines) + 1):
|
|
130
|
+
if lines[idx - 1].lstrip().startswith("## "):
|
|
131
|
+
end = idx
|
|
132
|
+
break
|
|
133
|
+
return (start, end)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def apply_migration_exclusion(
|
|
137
|
+
matches: list[dict], skill_md_source: str | None, skill_md_text: str | None
|
|
138
|
+
) -> tuple[list[dict], int]:
|
|
139
|
+
"""Drop matches whose source is the compiled SKILL.md and whose line sits
|
|
140
|
+
inside its Migration & Deprecation Warnings section. Returns (kept, dropped)."""
|
|
141
|
+
if skill_md_source is None or skill_md_text is None:
|
|
142
|
+
return matches, 0
|
|
143
|
+
window = migration_section_range(skill_md_text)
|
|
144
|
+
if window is None:
|
|
145
|
+
return matches, 0
|
|
146
|
+
start, end = window
|
|
147
|
+
kept, dropped = [], 0
|
|
148
|
+
for m in matches:
|
|
149
|
+
if m["source"] == skill_md_source and start <= m["line_number"] < end:
|
|
150
|
+
dropped += 1
|
|
151
|
+
continue
|
|
152
|
+
kept.append(m)
|
|
153
|
+
return kept, dropped
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _load(path: str) -> str | None:
|
|
157
|
+
"""Read a feeder file as UTF-8. Returns None for missing/empty (skip, not
|
|
158
|
+
an error). UTF-8 avoids cp1252 mojibake on Windows."""
|
|
159
|
+
p = Path(path)
|
|
160
|
+
if not p.is_file():
|
|
161
|
+
return None
|
|
162
|
+
text = p.read_text(encoding="utf-8")
|
|
163
|
+
if not text.strip():
|
|
164
|
+
return None
|
|
165
|
+
return text
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def scan_files(feeders: list[str], skill_md: str | None) -> dict:
|
|
169
|
+
"""Scan every feeder + the skill-md feeder, apply the §4b exclusion, return
|
|
170
|
+
the result object. Deterministic: feeders scanned in the given order."""
|
|
171
|
+
scanned: list[str] = []
|
|
172
|
+
matches: list[dict] = []
|
|
173
|
+
skill_md_text = None
|
|
174
|
+
|
|
175
|
+
ordered = list(feeders)
|
|
176
|
+
if skill_md is not None:
|
|
177
|
+
ordered.append(skill_md)
|
|
178
|
+
|
|
179
|
+
for path in ordered:
|
|
180
|
+
text = _load(path)
|
|
181
|
+
if text is None:
|
|
182
|
+
continue
|
|
183
|
+
scanned.append(path)
|
|
184
|
+
if path == skill_md:
|
|
185
|
+
skill_md_text = text
|
|
186
|
+
matches.extend(scan_text(text, path))
|
|
187
|
+
|
|
188
|
+
matches, excluded = apply_migration_exclusion(matches, skill_md, skill_md_text)
|
|
189
|
+
return {
|
|
190
|
+
"scanned": scanned,
|
|
191
|
+
"matches": matches,
|
|
192
|
+
"match_count": len(matches),
|
|
193
|
+
"excluded_count": excluded,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
198
|
+
parser = argparse.ArgumentParser(
|
|
199
|
+
prog="scan-doc-rot",
|
|
200
|
+
description=(
|
|
201
|
+
"Deterministic doc-rot correction-indicator scan (step-doc-rot.md §2): "
|
|
202
|
+
"case-insensitive substring match of feeder artifacts against the fixed "
|
|
203
|
+
"correction-pattern table, with the compiled SKILL.md's Migration & "
|
|
204
|
+
"Deprecation Warnings section excluded."
|
|
205
|
+
),
|
|
206
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
207
|
+
)
|
|
208
|
+
parser.add_argument(
|
|
209
|
+
"positional_feeders",
|
|
210
|
+
nargs="*",
|
|
211
|
+
metavar="FEEDER",
|
|
212
|
+
help="Feeder artifact paths (evidence-report.md, provenance-map.json, temporal files).",
|
|
213
|
+
)
|
|
214
|
+
parser.add_argument(
|
|
215
|
+
"--feeder",
|
|
216
|
+
action="append",
|
|
217
|
+
default=[],
|
|
218
|
+
dest="feeders",
|
|
219
|
+
help="Feeder artifact path (repeatable). Equivalent to a positional FEEDER.",
|
|
220
|
+
)
|
|
221
|
+
parser.add_argument(
|
|
222
|
+
"--skill-md",
|
|
223
|
+
dest="skill_md",
|
|
224
|
+
default=None,
|
|
225
|
+
help="Compiled/staged SKILL.md feeder; its Migration & Deprecation Warnings "
|
|
226
|
+
"section is excluded from matches.",
|
|
227
|
+
)
|
|
228
|
+
return parser
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def main(argv=None) -> int:
|
|
232
|
+
parser = _build_parser()
|
|
233
|
+
args = parser.parse_args(argv)
|
|
234
|
+
feeders = list(args.positional_feeders) + list(args.feeders)
|
|
235
|
+
if not feeders and args.skill_md is None:
|
|
236
|
+
parser.print_usage(file=sys.stderr)
|
|
237
|
+
print("error: supply at least one feeder path or --skill-md", file=sys.stderr)
|
|
238
|
+
return 1
|
|
239
|
+
result = scan_files(feeders, args.skill_md)
|
|
240
|
+
print(json.dumps(result, indent=2))
|
|
241
|
+
return 0
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
raise SystemExit(main())
|
|
@@ -12,6 +12,7 @@ Produces a consolidated stack skill documenting how libraries connect. **Code-mo
|
|
|
12
12
|
## Conventions
|
|
13
13
|
|
|
14
14
|
- Bare paths (e.g. `references/<name>.md`) resolve from the skill root.
|
|
15
|
+
- 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`.
|
|
15
16
|
- `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.
|
|
16
17
|
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).
|
|
17
18
|
- `{project-root}`-prefixed paths resolve from the project working directory.
|
|
@@ -34,7 +35,9 @@ These rules apply to every step in this workflow:
|
|
|
34
35
|
|
|
35
36
|
## Workflow state contract
|
|
36
37
|
|
|
37
|
-
Every step that emits a warning
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
**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.
|
|
38
41
|
|
|
39
42
|
## Stages
|
|
40
43
|
|
|
@@ -65,14 +68,13 @@ Every step that emits a warning ("log a warning", "record in workflow state for
|
|
|
65
68
|
|
|
66
69
|
Every HARD HALT in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
|
|
67
70
|
|
|
68
|
-
| Code | Meaning | Raised by
|
|
71
|
+
| Code | Meaning | Raised by (halt_reason) |
|
|
69
72
|
| ---- | -------------------- | ------------------------------------------------------------------------------------------ |
|
|
70
73
|
| 0 | success | step 10 (terminal handoff to shared health-check) |
|
|
71
|
-
| 2 | input
|
|
72
|
-
| 3 | resolution-failure | step 1 §1
|
|
73
|
-
| 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 |
|
|
74
|
-
|
|
|
75
|
-
| 6 | user-cancelled | any interactive menu in step 3 / step 6 (user selected `[X]` Cancel and exit) |
|
|
74
|
+
| 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`) |
|
|
75
|
+
| 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` |
|
|
76
|
+
| 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` |
|
|
77
|
+
| 6 | user-cancelled | any interactive menu in step 3 / step 6 when the user selects `[X]` Cancel and exit (`user-cancelled`) |
|
|
76
78
|
|
|
77
79
|
## Result Contract (Headless)
|
|
78
80
|
|
|
@@ -82,7 +84,7 @@ When `{headless_mode}` is true, step 9 emits a single-line JSON envelope on **st
|
|
|
82
84
|
SKF_STACK_RESULT_JSON: {"status":"success|error","skill_package":"…|null","skill_name":"…","stack_libraries":["…"],"mode":"code|compose","exit_code":0,"halt_reason":null}
|
|
83
85
|
```
|
|
84
86
|
|
|
85
|
-
`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. `halt_reason` is one of: `null` (success), `"
|
|
87
|
+
`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.
|
|
86
88
|
|
|
87
89
|
## On Activation
|
|
88
90
|
|
|
@@ -118,6 +120,10 @@ SKF_STACK_RESULT_JSON: {"status":"success|error","skill_package":"…|null","ski
|
|
|
118
120
|
- `{composeModeRulesPath}` ← `workflow.compose_mode_rules_path` if non-empty, else `references/compose-mode-rules.md`
|
|
119
121
|
- `{provenanceMapSchemaPath}` ← `workflow.provenance_map_schema_path` if non-empty, else `assets/provenance-map-schema.md`
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
Also resolve `{onCompleteCommand}` ← `workflow.on_complete` if non-empty, else empty string (no-op — `references/report.md` §6c skips the hook invocation entirely).
|
|
124
|
+
|
|
125
|
+
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.
|
|
126
|
+
|
|
127
|
+
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.
|
|
122
128
|
|
|
123
129
|
4. Load, read the full file, and then execute `references/init.md` to begin the workflow.
|
|
@@ -53,7 +53,7 @@ Used when the workflow ran in code-mode against an actual codebase. `source_repo
|
|
|
53
53
|
|
|
54
54
|
## Compose-mode variant
|
|
55
55
|
|
|
56
|
-
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 `
|
|
56
|
+
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).
|
|
57
57
|
|
|
58
58
|
```json
|
|
59
59
|
{
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# DO NOT EDIT -- overwritten on every update.
|
|
2
2
|
#
|
|
3
3
|
# Workflow customization surface for skf-create-stack-skill.
|
|
4
|
+
# Team overrides: _bmad/custom/skf-create-stack-skill.toml (under {project-root})
|
|
5
|
+
# Personal overrides: _bmad/custom/skf-create-stack-skill.user.toml (under {project-root})
|
|
4
6
|
|
|
5
7
|
[workflow]
|
|
6
8
|
|
|
@@ -34,6 +36,14 @@ persistent_facts = [
|
|
|
34
36
|
"file:{project-root}/**/project-context.md",
|
|
35
37
|
]
|
|
36
38
|
|
|
39
|
+
# Command invoked once the workflow reaches its terminal stage — after the
|
|
40
|
+
# stack package is committed and the result contract is written (step 9,
|
|
41
|
+
# report.md §6c), before the health-check chain. Override wins. Use for a
|
|
42
|
+
# terminal action such as catalog registration or notifying a downstream
|
|
43
|
+
# pipeline. Empty string = no terminal action. The hook never fails the run.
|
|
44
|
+
|
|
45
|
+
on_complete = ""
|
|
46
|
+
|
|
37
47
|
# --- Optional asset overrides ---
|
|
38
48
|
#
|
|
39
49
|
# Lift the canonical asset paths so orgs can substitute house-style copies
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'generate-output.md'
|
|
3
|
-
stackSkillTemplate: 'assets/stack-skill-template.md'
|
|
4
3
|
---
|
|
5
4
|
|
|
6
5
|
<!-- Config: communicate in {communication_language}. Artifact text in {document_output_language}. -->
|
|
@@ -13,7 +12,7 @@ Assemble the main SKILL.md by combining per-library extractions with the integra
|
|
|
13
12
|
|
|
14
13
|
## Rules
|
|
15
14
|
|
|
16
|
-
- Compile SKILL.md following the stack-skill-template structure — integration patterns go first
|
|
15
|
+
- Compile SKILL.md following the stack-skill-template structure — integration patterns go first
|
|
17
16
|
- Do not write output files (Step 07)
|
|
18
17
|
- Present compiled content for user review
|
|
19
18
|
|
|
@@ -21,7 +20,7 @@ Assemble the main SKILL.md by combining per-library extractions with the integra
|
|
|
21
20
|
|
|
22
21
|
### 1. Load Template Structure
|
|
23
22
|
|
|
24
|
-
Load `{
|
|
23
|
+
Load `{stackSkillTemplatePath}` and prepare SKILL.md section structure.
|
|
25
24
|
|
|
26
25
|
### 2. Generate Frontmatter
|
|
27
26
|
|
|
@@ -41,12 +40,12 @@ description: >
|
|
|
41
40
|
**Frontmatter rules:**
|
|
42
41
|
|
|
43
42
|
- `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.
|
|
44
|
-
- `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 `{
|
|
43
|
+
- `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}`.
|
|
45
44
|
- No other frontmatter fields — only `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools` are permitted by spec
|
|
46
45
|
|
|
47
46
|
### 3. Compile Integration Layer
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
Compile in order:
|
|
50
49
|
|
|
51
50
|
**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).
|
|
52
51
|
|
|
@@ -72,7 +71,7 @@ description: >
|
|
|
72
71
|
`Per-Library Summaries` and `Library Reference Index` are the largest sections
|
|
73
72
|
and grow with the stack. For a **large stack** (heuristic: **> 6 libraries OR
|
|
74
73
|
> 6 integration patterns**), author both into `references/stack-catalog.md`
|
|
75
|
-
(structure in `{
|
|
74
|
+
(structure in `{stackSkillTemplatePath}`) and place only the inline pointer from the
|
|
76
75
|
template's "Sizing Guidance" in SKILL.md — this keeps the body under the 500-line
|
|
77
76
|
`body.max_lines` budget that step 08 enforces. For a **small stack**, keep both
|
|
78
77
|
inline (inline passive context yields higher task accuracy). Either way, step 08's
|
|
@@ -132,9 +131,9 @@ Display: **Select:** [C] Continue to Output Generation | [X] Cancel and exit
|
|
|
132
131
|
|
|
133
132
|
#### EXECUTION RULES:
|
|
134
133
|
|
|
135
|
-
-
|
|
134
|
+
- 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.
|
|
136
135
|
- **GATE [default: C]** — If `{headless_mode}`: auto-proceed with [C] Continue, log: "headless: auto-approve stack compilation"
|
|
137
|
-
-
|
|
136
|
+
- Proceed to the next step only once the user approves by selecting `C`.
|
|
138
137
|
|
|
139
138
|
#### Menu Handling Logic:
|
|
140
139
|
|
|
@@ -6,18 +6,7 @@ Rules for synthesizing a stack skill from pre-generated individual skills and an
|
|
|
6
6
|
|
|
7
7
|
## Skill Loading
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**Version-aware skill enumeration:**
|
|
12
|
-
|
|
13
|
-
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`.
|
|
14
|
-
2. **Fallback: `active` symlinks** — If the manifest does not exist, is empty, or a manifest entry lacks an `active_version`, 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).
|
|
15
|
-
3. **Filter:** Skip any skill named `{project_name}-stack` or any skill where `metadata.json` has `"skill_type": "stack"` — stack skills must not be loaded as source dependencies to avoid self-referencing loops
|
|
16
|
-
4. Skip any resolved skill package missing either `SKILL.md` or `metadata.json` — log a warning and skip
|
|
17
|
-
5. Load each `metadata.json` from the resolved version-aware path and extract: `name`, `language`, `confidence_tier`, `source_repo`, `exports` (count), `version`
|
|
18
|
-
6. 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)
|
|
19
|
-
7. Store the resolved package path as `skill_package_path` for use in later steps (extraction, integration detection)
|
|
20
|
-
8. Store loaded skills as `raw_dependencies` with `source: "existing_skill"`
|
|
9
|
+
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.
|
|
21
10
|
|
|
22
11
|
## Compose-mode Co-mention Precision
|
|
23
12
|
|
|
@@ -86,4 +75,4 @@ When no architecture document is available:
|
|
|
86
75
|
- Mark all inferred integrations: `[inferred from shared domain]`
|
|
87
76
|
- Inferred integrations default to lowest confidence of the pair with `[inferred from shared domain]` suffix (use this instead of `[composed]` for inferred integrations)
|
|
88
77
|
|
|
89
|
-
**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 `
|
|
78
|
+
**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.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'compile-stack.md'
|
|
3
|
-
integrationPatterns: 'references/integration-patterns.md'
|
|
4
|
-
composeModeRules: 'references/compose-mode-rules.md'
|
|
5
3
|
pairIntersectProbeOrder:
|
|
6
4
|
- '{project-root}/_bmad/skf/shared/scripts/skf-pair-intersect.py'
|
|
7
5
|
- '{project-root}/src/shared/scripts/skf-pair-intersect.py'
|
|
6
|
+
comentionProbeOrder:
|
|
7
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-comention-pairs.py'
|
|
8
|
+
- '{project-root}/src/shared/scripts/skf-comention-pairs.py'
|
|
9
|
+
validateFeasibilityReportProbeOrder:
|
|
10
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-feasibility-report.py'
|
|
11
|
+
- '{project-root}/src/shared/scripts/skf-validate-feasibility-report.py'
|
|
8
12
|
---
|
|
9
13
|
|
|
10
14
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -19,7 +23,6 @@ Analyze co-import patterns between confirmed libraries to identify integration p
|
|
|
19
23
|
|
|
20
24
|
- Focus on detecting cross-library patterns using subprocess Pattern 1 (grep/search)
|
|
21
25
|
- Do not compile SKILL.md (Step 06)
|
|
22
|
-
- Integration detection is the core differentiator of stack skills vs individual skills
|
|
23
26
|
|
|
24
27
|
## MANDATORY SEQUENCE
|
|
25
28
|
|
|
@@ -54,7 +57,7 @@ From `confirmed_dependencies`, conceptually you have N*(N-1)/2 unordered pairs.
|
|
|
54
57
|
`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.
|
|
55
58
|
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.
|
|
56
59
|
|
|
57
|
-
**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
|
|
60
|
+
**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.
|
|
58
61
|
|
|
59
62
|
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)**...**"
|
|
60
63
|
|
|
@@ -64,29 +67,54 @@ Report: "**Analyzing {pair_count} library pairs for integration patterns** (prun
|
|
|
64
67
|
|
|
65
68
|
Instead of co-import grep, detect integrations from architecture document:
|
|
66
69
|
|
|
67
|
-
1. Load `{
|
|
70
|
+
1. Load `{composeModeRulesPath}` for integration evidence format rules
|
|
68
71
|
2. **If `{architecture_doc_path}` is null or not available:** Skip directly to the "If no architecture document available" fallback below
|
|
69
|
-
3.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
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".
|
|
73
|
+
|
|
74
|
+
**Resolve `{comentionHelper}`** from `{comentionProbeOrder}`; first existing path wins.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uv run {comentionHelper} comention --doc {architecture_doc_path} --skills -
|
|
78
|
+
```
|
|
79
|
+
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:
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"pairs": [
|
|
83
|
+
{"a": "<skill>", "b": "<skill>", "paragraph_count": N,
|
|
84
|
+
"evidence": [{"header": "<governing-header-or-null>", "excerpt": "<text>"}, ...]},
|
|
85
|
+
...
|
|
86
|
+
],
|
|
87
|
+
"excluded_section_count": M
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
`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.
|
|
91
|
+
|
|
92
|
+
**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.
|
|
93
|
+
4. For each detected integration pair (from `pairs[]`):
|
|
76
94
|
- Load both skills' export lists and API signatures
|
|
77
|
-
- Compose an integration section following the format from `{
|
|
95
|
+
- Compose an integration section following the format from `{composeModeRulesPath}`
|
|
78
96
|
- 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.
|
|
79
97
|
- Cite evidence from both skills: `[from skill: {skill_name}]`
|
|
80
98
|
|
|
81
|
-
All integration evidence inherits confidence tiers from the source skills. Load and apply the full **Confidence Tier Inheritance** matrix from `{
|
|
99
|
+
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]`.
|
|
82
100
|
|
|
83
101
|
**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:
|
|
84
102
|
|
|
85
103
|
- 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).
|
|
86
|
-
- **Schema version guard
|
|
104
|
+
- **Schema version guard (deterministic gate):** Resolve `{validateFeasibilityReportHelper}` from `{validateFeasibilityReportProbeOrder}` (first existing path wins) and run it against the located report:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python3 {validateFeasibilityReportHelper} <located-report-path>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
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.
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
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"}
|
|
114
|
+
```
|
|
87
115
|
- Read `overallVerdict` from frontmatter (exactly one of `FEASIBLE|CONDITIONALLY_FEASIBLE|NOT_FEASIBLE`).
|
|
88
116
|
- 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.
|
|
89
|
-
- For each architecture-detected pair, include `VS overall: {overallVerdict}` and `VS pair: {verdict}` in the integration evidence per the format in `{
|
|
117
|
+
- 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.
|
|
90
118
|
|
|
91
119
|
If no architecture document available:
|
|
92
120
|
- 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`)
|
|
@@ -115,7 +143,7 @@ If `tools.ccc` is true AND `ccc_index.status` is `"fresh"` or `"stale"` in forge
|
|
|
115
143
|
|
|
116
144
|
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.
|
|
117
145
|
|
|
118
|
-
**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 `
|
|
146
|
+
**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.
|
|
119
147
|
|
|
120
148
|
**Tool resolution for ccc_bridge.search:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `ccc search "{libA} {libB}" --path {source_root} --top 10` (CLI). See `knowledge/tool-resolution.md`.
|
|
121
149
|
|
|
@@ -125,16 +153,9 @@ CCC failures: skip augmentation silently, proceed with grep-only results.
|
|
|
125
153
|
|
|
126
154
|
### 3. Classify Integration Types
|
|
127
155
|
|
|
128
|
-
Load `{
|
|
156
|
+
Load `{integrationPatternsPath}` for classification rules.
|
|
129
157
|
|
|
130
|
-
For each qualifying pair, analyze to classify the integration type (**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):
|
|
131
|
-
|
|
132
|
-
- **Type 1: Middleware Chain** — Sequential function calls piping output between libraries
|
|
133
|
-
- **Type 2: Shared Types** — Type definitions exchanged between libraries
|
|
134
|
-
- **Type 3: Configuration Bridge** — One library configuring or initializing another
|
|
135
|
-
- **Type 4: Event Handler** — Event patterns crossing library boundaries
|
|
136
|
-
- **Type 5: Adapter/Wrapper** — Thin wrapper connecting library interfaces
|
|
137
|
-
- **Type 6: State Sharing** — Shared state stores or context providers
|
|
158
|
+
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):
|
|
138
159
|
|
|
139
160
|
For each detected integration:
|
|
140
161
|
- Identify the top 3 files demonstrating the pattern
|
|
@@ -145,7 +166,7 @@ For each detected integration:
|
|
|
145
166
|
- `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`.
|
|
146
167
|
- `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`.
|
|
147
168
|
- `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`.
|
|
148
|
-
- 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 `
|
|
169
|
+
- 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.
|
|
149
170
|
|
|
150
171
|
**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.
|
|
151
172
|
|
|
@@ -159,33 +180,7 @@ Assemble the integration graph:
|
|
|
159
180
|
|
|
160
181
|
### 5. Display Integration Summary
|
|
161
182
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
"**Integration detection complete.**
|
|
165
|
-
|
|
166
|
-
**Integration graph:** {lib_count} libraries, {pair_count} integration pairs
|
|
167
|
-
|
|
168
|
-
**Hub libraries** (connected to 3+ others):
|
|
169
|
-
{For each hub:} - **{library}** — integrates with {partner_list}
|
|
170
|
-
|
|
171
|
-
**Detected integrations:**
|
|
172
|
-
| Library A | Library B | Type | Co-import Files | Confidence |
|
|
173
|
-
|-----------|-----------|------|-----------------|------------|
|
|
174
|
-
| {name} | {name} | {type} | {count} | {tier} |
|
|
175
|
-
|
|
176
|
-
{If cross-cutting patterns:}
|
|
177
|
-
**Cross-cutting patterns:**
|
|
178
|
-
- {description spanning 3+ libraries}
|
|
179
|
-
|
|
180
|
-
**Proceeding to stack compilation...**"
|
|
181
|
-
|
|
182
|
-
**If no integrations detected:**
|
|
183
|
-
|
|
184
|
-
"**No co-import integration patterns detected** between confirmed libraries.
|
|
185
|
-
|
|
186
|
-
The libraries in this project appear to operate independently. The stack skill will contain library summaries without an integration layer.
|
|
187
|
-
|
|
188
|
-
**Proceeding to stack compilation...**"
|
|
183
|
+
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.
|
|
189
184
|
|
|
190
185
|
### 6. Auto-Proceed to Next Step
|
|
191
186
|
|