bmad-module-skill-forge 1.0.0 → 1.2.0
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/README.md +9 -7
- package/docs/_data/pinned.yaml +5 -2
- package/docs/{RELEASING.md → _internal/RELEASING.md} +87 -61
- package/docs/{STABILITY.md → _internal/STABILITY.md} +2 -2
- package/docs/bmad-synergy.md +2 -2
- package/docs/getting-started.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/why-skf.md +5 -4
- package/package.json +2 -7
- package/release-audits/v1.0.0-launch-audit.md +313 -2
- package/src/knowledge/ccc-bridge.md +1 -1
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +134 -0
- package/src/shared/scripts/skf-detect-tools.py +359 -0
- package/src/shared/scripts/skf-emit-result-envelope.py +419 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +413 -0
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +324 -0
- package/src/shared/scripts/skf-preflight.py +14 -4
- package/src/shared/scripts/skf-qmd-classify-collections.py +212 -0
- package/src/shared/scripts/skf-validate-frontmatter.py +9 -3
- package/src/skf-audit-skill/steps-c/step-01-init.md +60 -0
- package/src/skf-audit-skill/steps-c/step-02-re-index.md +7 -1
- package/src/skf-audit-skill/steps-c/step-03-structural-diff.md +19 -1
- package/src/skf-audit-skill/steps-c/step-04-semantic-diff.md +10 -1
- package/src/skf-audit-skill/steps-c/step-05-severity-classify.md +8 -0
- package/src/skf-audit-skill/steps-c/step-06-report.md +8 -0
- package/src/skf-brief-skill/assets/skill-brief-schema.md +36 -18
- package/src/skf-create-skill/steps-c/step-06-validate.md +1 -1
- package/src/skf-drop-skill/steps-c/step-02-execute.md +1 -1
- package/src/skf-export-skill/assets/managed-section-format.md +1 -1
- package/src/skf-export-skill/steps-c/step-04-update-context.md +4 -4
- package/src/skf-rename-skill/steps-c/step-02-execute.md +1 -1
- package/src/skf-setup/SKILL.md +25 -10
- package/src/skf-setup/references/tier-rules.md +2 -2
- package/src/skf-setup/steps-c/step-01-detect-and-tier.md +58 -71
- package/src/skf-setup/steps-c/step-01b-ccc-index.md +49 -66
- package/src/skf-setup/steps-c/step-02-write-config.md +59 -86
- package/src/skf-setup/steps-c/step-03-auto-index.md +73 -91
- package/src/skf-setup/steps-c/step-04-report.md +135 -11
- package/src/skf-setup/steps-c/step-05-health-check.md +4 -2
- package/src/skf-test-skill/steps-c/step-01-init.md +4 -4
- package/src/skf-test-skill/steps-c/step-03-coverage-check.md +1 -1
- package/src/skf-update-skill/steps-c/step-02-detect-changes.md +123 -0
- package/tools/validate-docs-drift.js +42 -2
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: './step-02-write-config.md'
|
|
3
|
+
# Resolve `{mergeCccExclusionsHelper}` by probing `{mergeCccExclusionsProbeOrder}`
|
|
4
|
+
# in order (installed SKF module path first, src/ dev-checkout fallback); first
|
|
5
|
+
# existing path wins. HALT if neither resolves — the script owns config-value
|
|
6
|
+
# validation and the set-union merge into .cocoindex_code/settings.yml; no
|
|
7
|
+
# fallback to prose-driven validation.
|
|
8
|
+
mergeCccExclusionsProbeOrder:
|
|
9
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-merge-ccc-exclusions.py'
|
|
10
|
+
- '{project-root}/src/shared/scripts/skf-merge-ccc-exclusions.py'
|
|
3
11
|
---
|
|
4
12
|
|
|
13
|
+
<!-- Config: communicate in {communication_language}. User-visible status messages (CCC exclusion summary, indexing progress message) render in the user's language. -->
|
|
14
|
+
|
|
5
15
|
# Step 1b: CCC Index Verification
|
|
6
16
|
|
|
7
17
|
## STEP GOAL:
|
|
8
18
|
|
|
9
|
-
If ccc is available (`{ccc: true}` from step-01),
|
|
19
|
+
If ccc is available (`{ccc: true}` from step-01), invoke `{mergeCccExclusionsHelper}` to validate config-driven SKF exclusion patterns and merge them into `.cocoindex_code/settings.yml`, then verify the project's ccc index exists and create or refresh it if needed. Store index state and exclusion-merge results in context for step-02 to write into forge-tier.yaml and for step-04 to surface in the JSON envelope.
|
|
10
20
|
|
|
11
21
|
For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed.
|
|
12
22
|
|
|
13
23
|
## Rules
|
|
14
24
|
|
|
15
|
-
- Focus only on ccc index verification and
|
|
25
|
+
- Focus only on ccc index verification, exclusion-pattern merge, and (re-)indexing
|
|
16
26
|
- Do not display skip messages for Quick/Forge tiers
|
|
17
27
|
- Do not fail the workflow if ccc indexing fails
|
|
18
|
-
-
|
|
28
|
+
- Never reimplement the exclusion-pattern validation in prose — the script owns the PR #248 rules
|
|
19
29
|
|
|
20
30
|
## MANDATORY SEQUENCE
|
|
21
31
|
|
|
22
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
23
|
-
|
|
24
32
|
### 1. Check Eligibility
|
|
25
33
|
|
|
26
34
|
Read `{ccc}` from step-01 context.
|
|
27
35
|
|
|
28
|
-
**If `{ccc}` is false:** Set `{ccc_index_result: "none", ccc_indexed_path: null, ccc_last_indexed: null, ccc_exclude_patterns: []}`. Proceed directly to section 4 (Auto-Proceed) — no output, no messaging.
|
|
36
|
+
**If `{ccc}` is false:** Set `{ccc_index_result: "none", ccc_indexed_path: null, ccc_last_indexed: null, ccc_exclude_patterns: [], ccc_exclusion_warnings: [], settings_yml_written: false, settings_yml_patterns_added: 0}`. Proceed directly to section 4 (Auto-Proceed) — no output, no messaging.
|
|
29
37
|
|
|
30
38
|
**If `{ccc}` is true:** Continue to section 2.
|
|
31
39
|
|
|
@@ -36,81 +44,68 @@ Read existing forge-tier.yaml at `{project-root}/_bmad/_memory/forger-sidecar/fo
|
|
|
36
44
|
Read `staleness_threshold_hours` from `ccc_index.staleness_threshold_hours` in the existing forge-tier.yaml (default: 24 if not present or not a number). Use this value for the freshness check below.
|
|
37
45
|
|
|
38
46
|
Check the `ccc_index` section:
|
|
47
|
+
|
|
39
48
|
- If `ccc_index.indexed_path` matches `{project-root}` AND `ccc_index.status` is `"fresh"` or `"created"`:
|
|
40
49
|
- Check freshness: if `ccc_index.last_indexed` is within `staleness_threshold_hours` of now → index is fresh
|
|
41
50
|
- Store `{ccc_index_result: "fresh", ccc_indexed_path: {project-root}, ccc_last_indexed: {existing timestamp}}`
|
|
42
|
-
- Set `{needs_reindex: false}` —
|
|
51
|
+
- Set `{needs_reindex: false}` — exclusions still get merged in section 3, which may force a re-index
|
|
43
52
|
|
|
44
53
|
- If `ccc_index.indexed_path` matches `{project-root}` but timestamp is older than threshold:
|
|
45
|
-
- Set `{needs_reindex: true}`
|
|
54
|
+
- Set `{needs_reindex: true}`
|
|
46
55
|
|
|
47
56
|
- If `ccc_index` is missing, has null values, or path doesn't match:
|
|
48
|
-
- Set `{needs_reindex: true}`
|
|
57
|
+
- Set `{needs_reindex: true}`
|
|
49
58
|
|
|
50
|
-
###
|
|
59
|
+
### 3. Merge SKF Exclusion Patterns
|
|
51
60
|
|
|
52
61
|
SKF infrastructure and output directories must be excluded from the CCC index — they contain workflow instructions, build artifacts, and generated skills that pollute semantic search results with zero extraction value.
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
1. Use `{skills_output_folder}` and `{forge_data_folder}` from the workflow activation context (resolved in On Activation from `{project-root}/_bmad/skf/config.yaml`).
|
|
57
|
-
|
|
58
|
-
2. Assemble the exclusion patterns using `**/` prefix format (matching `.cocoindex_code/settings.yml` convention — e.g., `**/node_modules`):
|
|
59
|
-
- `**/_bmad` — SKF framework module (workflows, agents, knowledge files)
|
|
60
|
-
- `**/_bmad-output` — Build output artifacts
|
|
61
|
-
- `**/.claude` — Claude Code configuration
|
|
62
|
-
- `**/_skf-learn` — SKF learning materials
|
|
63
|
-
- `**/{skills_output_folder}` — Generated skill files (from activation context)
|
|
64
|
-
- `**/{forge_data_folder}` — Compilation workspace (from activation context)
|
|
65
|
-
|
|
66
|
-
3. Store `{ccc_exclude_patterns}` in context for step-02 to write into forge-tier.yaml.
|
|
63
|
+
Run the merge helper, forwarding the resolved config values from the workflow activation context. Invoke via `uv run` so PEP 723 inline metadata resolves the script's PyYAML dependency automatically (per `docs/getting-started.md`'s prereq list — uv exists for this exact purpose). Bare `python3` will fail on a fresh Python with `ModuleNotFoundError: No module named 'yaml'`.
|
|
67
64
|
|
|
68
|
-
|
|
65
|
+
```bash
|
|
66
|
+
uv run {mergeCccExclusionsHelper} \
|
|
67
|
+
--project-root "{project-root}" \
|
|
68
|
+
--skills-output-folder "{skills_output_folder}" \
|
|
69
|
+
--forge-data-folder "{forge_data_folder}"
|
|
70
|
+
```
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
The script (see `src/shared/scripts/skf-merge-ccc-exclusions.py` docstring for the full schema) builds the SKF exclusion list (4 always-include hardcoded patterns + 2 conditional from validated config), applies the PR #248 validation rules to reject empty / absolute / glob-meta config values with actionable warnings, and performs an idempotent set-union merge into `{project-root}/.cocoindex_code/settings.yml`. User customizations are preserved. When the file does not exist yet (first-time setup before `ccc init`) the script creates it; when nothing new needs adding the script skips the write entirely (mtime preserved).
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
**Parse the JSON output and set context flags:**
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
4.
|
|
76
|
+
- `{settings_yml_existed}` ← `settings_yml_existed`
|
|
77
|
+
- `{settings_yml_written}` ← `written`
|
|
78
|
+
- `{settings_yml_patterns_added}` ← `patterns_added`
|
|
79
|
+
- `{ccc_exclude_patterns}` ← derive from the script's behaviour: it always merges the 4 always-include patterns plus any of the 2 config-driven ones whose value passed validation. Step-02 needs this list to write `ccc_index.exclude_patterns` into forge-tier.yaml — read it from the script's `patterns_added_list` PLUS any already-present matching SKF patterns inferred from the input. Pragmatically: re-derive the list as `["**/_bmad", "**/_bmad-output", "**/.claude", "**/_skf-learn"]` plus `"**/{skills_output_folder}"` if no warning names `skills_output_folder` plus `"**/{forge_data_folder}"` if no warning names `forge_data_folder`.
|
|
80
|
+
- `{ccc_exclusion_warnings}` ← `warnings` (a list — step-04 folds them into the envelope's warnings array)
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
**If `{settings_yml_written}` is true** (new patterns merged into settings.yml): set `{needs_reindex: true}` — new exclusions require re-indexing for the index to reflect them. Display: "**CCC exclusions configured:** {patterns_added} SKF patterns applied to .cocoindex_code/settings.yml"
|
|
80
83
|
|
|
81
|
-
If `{
|
|
84
|
+
**If `{settings_yml_written}` is false** (idempotent re-run, all patterns already present): display nothing (exclusions already configured). Do NOT change `{needs_reindex}` — it stays at whatever section 2 set it to.
|
|
82
85
|
|
|
83
86
|
**Flow decision:**
|
|
84
|
-
- If `{needs_reindex}` is true: proceed to section 3
|
|
85
|
-
- If `{needs_reindex}` is false: proceed to section 4 (Auto-Proceed)
|
|
86
|
-
|
|
87
|
-
### 3. Create or Refresh CCC Index
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
- If `{needs_reindex}` is true: proceed to section 4
|
|
89
|
+
- If `{needs_reindex}` is false: proceed to section 5 (Auto-Proceed)
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
### 4. Create or Refresh CCC Index
|
|
92
92
|
|
|
93
|
-
**If `{ccc_daemon}` is `"
|
|
93
|
+
**If `{ccc_daemon}` is `"stopped"` or `"healthy"`:** the `ccc index` command auto-starts the daemon when needed.
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
**If `{ccc_daemon}` is `"error"`:** attempt indexing anyway — errors will be caught below.
|
|
96
96
|
|
|
97
97
|
Run (CWD must be `{project-root}`):
|
|
98
|
+
|
|
98
99
|
```bash
|
|
99
100
|
ccc init
|
|
100
101
|
```
|
|
101
102
|
|
|
102
|
-
**If init fails** (project may already be initialized): continue — this is not an error.
|
|
103
|
-
|
|
104
|
-
**Apply SKF exclusion patterns (if not already applied in section 2b):**
|
|
103
|
+
**If init fails** (project may already be initialized): continue — this is not an error. Note: when `{settings_yml_existed}` was false and `ccc init` just created the settings.yml, the merge in section 3 ran BEFORE ccc init. The script handles "no settings.yml exists" by creating one with just the SKF exclusions, which `ccc init` will then either preserve (if it merges) or overwrite (in which case the next workflow run re-merges). Either way the SKF exclusions end up in the file by the time `ccc index` runs the second time.
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
1. Read `{project-root}/.cocoindex_code/settings.yml` (created by `ccc init`)
|
|
109
|
-
2. For each pattern in `{ccc_exclude_patterns}`: if the pattern is NOT already present in `exclude_patterns`, append it
|
|
110
|
-
3. Write the updated `settings.yml` back
|
|
111
|
-
4. Display: "**CCC exclusions configured:** {count} SKF patterns applied to .cocoindex_code/settings.yml"
|
|
105
|
+
Before invoking `ccc index`, display: "**Building semantic index — this can take several minutes on large codebases (1000+ files). Run `ccc status` in another terminal to monitor progress.**" so the user does not assume the workflow has hung during the long-running call.
|
|
112
106
|
|
|
113
107
|
Then run:
|
|
108
|
+
|
|
114
109
|
```bash
|
|
115
110
|
ccc index
|
|
116
111
|
```
|
|
@@ -118,29 +113,17 @@ ccc index
|
|
|
118
113
|
**Note:** `ccc index` can take several minutes on large codebases (1000+ files). Run with an extended timeout or in background mode. Use `ccc status` to verify completion — check that `Chunks` and `Files` counts are non-zero.
|
|
119
114
|
|
|
120
115
|
**If succeeds:**
|
|
116
|
+
|
|
121
117
|
- Run `ccc status` to get file count
|
|
122
118
|
- Store `{ccc_index_result: "created", ccc_indexed_path: {project-root}, ccc_last_indexed: {current ISO timestamp}, ccc_file_count: {count from ccc status}}`
|
|
123
119
|
- Display: "**CCC index created.** {ccc_file_count} files indexed for semantic discovery."
|
|
124
120
|
|
|
125
121
|
**If fails:**
|
|
126
|
-
|
|
122
|
+
|
|
123
|
+
- Store `{ccc_index_result: "failed", ccc_indexed_path: null, ccc_last_indexed: null, ccc_indexing_failed_reason: {error}}` (the failed-reason flag flows into step-04's envelope warnings)
|
|
127
124
|
- Display: "CCC indexing failed: {error}. Extraction will use direct AST scanning — semantic pre-ranking unavailable this session."
|
|
128
125
|
- Continue — this is NOT a workflow error
|
|
129
126
|
|
|
130
|
-
###
|
|
131
|
-
|
|
132
|
-
"**Proceeding to write configuration...**"
|
|
133
|
-
|
|
134
|
-
#### Menu Handling Logic:
|
|
135
|
-
|
|
136
|
-
- After ccc index check completes (or is skipped), immediately load, read entire file, then execute {nextStepFile}
|
|
137
|
-
|
|
138
|
-
#### EXECUTION RULES:
|
|
139
|
-
|
|
140
|
-
- This is an auto-proceed step with no user choices
|
|
141
|
-
- Proceed directly to next step after ccc index verification
|
|
142
|
-
|
|
143
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
144
|
-
|
|
145
|
-
ONLY WHEN ccc index verification is complete (or step is skipped for ccc unavailable) will you load and read fully `{nextStepFile}` to execute the configuration write step.
|
|
127
|
+
### 5. Auto-Proceed
|
|
146
128
|
|
|
129
|
+
After ccc index verification is complete (or skipped because ccc is unavailable), display "**Proceeding to write configuration...**", then load `{nextStepFile}`, read it fully, and execute it.
|
|
@@ -1,123 +1,96 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: './step-03-auto-index.md'
|
|
3
|
+
# Resolve `{forgeTierRwHelper}` by probing `{forgeTierRwProbeOrder}` in order
|
|
4
|
+
# (installed SKF module path first, src/ dev-checkout fallback); first existing
|
|
5
|
+
# path wins. HALT if neither resolves — the script owns the canonical
|
|
6
|
+
# forge-tier.yaml format AND the array-preservation contract that protects
|
|
7
|
+
# qmd_collections / ccc_index_registry / staleness_threshold_hours from being
|
|
8
|
+
# lost on rewrite. NEVER fall back to inline YAML emission — drift between the
|
|
9
|
+
# script and a prose-rendered template will silently corrupt downstream skills.
|
|
10
|
+
forgeTierRwProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
|
|
3
13
|
---
|
|
4
14
|
|
|
15
|
+
<!-- Config: communicate in {communication_language}. Halt-on-write-failure messages render in the user's language. -->
|
|
16
|
+
|
|
5
17
|
# Step 2: Write Configuration
|
|
6
18
|
|
|
7
19
|
## STEP GOAL:
|
|
8
20
|
|
|
9
|
-
Write the detected tool availability and calculated tier to forge-tier.yaml, create preferences.yaml with defaults if it does not exist, and ensure the forge-data
|
|
21
|
+
Write the detected tool availability and calculated tier to `forge-tier.yaml` (preserving registry arrays from any existing file), create `preferences.yaml` with first-run defaults if it does not exist, and ensure the `forge-data/` directory is present. All file mutations go through `{forgeTierRwHelper}` so the format is locked, atomic, and array-preservation is guaranteed.
|
|
10
22
|
|
|
11
23
|
## Rules
|
|
12
24
|
|
|
13
25
|
- Focus only on writing configuration files and creating directories
|
|
14
26
|
- Do not re-detect tools — use results from step-01
|
|
15
|
-
-
|
|
16
|
-
- File write failures are errors — report clearly
|
|
27
|
+
- Never inline a YAML template for forge-tier.yaml or preferences.yaml — the script owns the canonical format
|
|
28
|
+
- File write failures are errors — report clearly and halt the workflow
|
|
17
29
|
|
|
18
30
|
## MANDATORY SEQUENCE
|
|
19
31
|
|
|
20
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
21
|
-
|
|
22
32
|
### 1. Write forge-tier.yaml
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
status: {ccc_index_result from step-01b: "fresh"|"created"|"none"|"failed"}
|
|
49
|
-
staleness_threshold_hours: 24
|
|
50
|
-
file_count: {ccc_file_count from step-01b, or ~}
|
|
51
|
-
exclude_patterns: {ccc_exclude_patterns from step-01b, or []}
|
|
52
|
-
|
|
53
|
-
# CCC index registry (tracks which source paths have been indexed for skill workflows)
|
|
54
|
-
# PRESERVE existing entries on re-runs — see Note below
|
|
55
|
-
ccc_index_registry: {preserved from existing forge-tier.yaml, or [] if first run}
|
|
56
|
-
|
|
57
|
-
# QMD collection registry (populated by create-skill, consumed by audit/update-skill)
|
|
58
|
-
# PRESERVE existing entries on re-runs — see Note below
|
|
59
|
-
qmd_collections: {preserved from existing forge-tier.yaml, or [] if first run}
|
|
34
|
+
Build the JSON payload from context flags set by step-01 and step-01b. The payload must include `tools`, `tier`, and `ccc_index`; the script handles `tier_detected_at` defaulting to "now" if absent and preserves `qmd_collections`, `ccc_index_registry`, and a user-customized `ccc_index.staleness_threshold_hours` from any existing file.
|
|
35
|
+
|
|
36
|
+
Invoke via `uv run` so the script's PEP 723 PyYAML dependency resolves automatically (this is what `docs/getting-started.md`'s uv prereq exists for). Bare `python3` would fail on a fresh interpreter with `ModuleNotFoundError`.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
echo '{
|
|
40
|
+
"tools": {
|
|
41
|
+
"ast_grep": {ast_grep},
|
|
42
|
+
"gh_cli": {gh_cli},
|
|
43
|
+
"qmd": {qmd},
|
|
44
|
+
"ccc": {ccc},
|
|
45
|
+
"ccc_daemon": {ccc_daemon},
|
|
46
|
+
"security_scan": {security_scan}
|
|
47
|
+
},
|
|
48
|
+
"tier": "{calculated_tier}",
|
|
49
|
+
"ccc_index": {
|
|
50
|
+
"indexed_path": {ccc_indexed_path},
|
|
51
|
+
"last_indexed": {ccc_last_indexed},
|
|
52
|
+
"status": "{ccc_index_result}",
|
|
53
|
+
"file_count": {ccc_file_count},
|
|
54
|
+
"exclude_patterns": {ccc_exclude_patterns}
|
|
55
|
+
}
|
|
56
|
+
}' | uv run {forgeTierRwHelper} write-tools \
|
|
57
|
+
--target "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml"
|
|
60
58
|
```
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
**This file is ALWAYS overwritten** on every run — it reflects current tool state.
|
|
65
|
-
|
|
66
|
-
If the write fails, report the error and halt the workflow.
|
|
60
|
+
The script atomically writes the file via temp + fsync + rename (mirrors `skf-atomic-write.py`'s crash-safety contract) and returns a JSON response with `wrote`, `preserved_arrays.qmd_collections` count, `preserved_arrays.ccc_index_registry` count, and the resolved `tier`.
|
|
67
61
|
|
|
68
|
-
|
|
62
|
+
**Parse the response and set context flags for step-04:**
|
|
69
63
|
|
|
70
|
-
|
|
64
|
+
- `{forge_tier_yaml_path}` ← `wrote`
|
|
65
|
+
- `{forge_tier_qmd_collections_count}` ← `preserved_arrays.qmd_collections`
|
|
66
|
+
- `{forge_tier_ccc_registry_count}` ← `preserved_arrays.ccc_index_registry`
|
|
71
67
|
|
|
72
|
-
**If
|
|
68
|
+
**If the script exits non-zero**: parse the stderr JSON `{"status":"error","message":...}`, set `{error: {phase: "step-02:write-tools", path: "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml", reason: <message>}}` for step-04's envelope, and halt the workflow before chaining to step-03.
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
# Ferris Sidecar: User Preferences
|
|
76
|
-
# Created by setup workflow on first run
|
|
77
|
-
# Edit this file to customize Ferris behavior
|
|
70
|
+
### 2. Initialize preferences.yaml
|
|
78
71
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
passive_context: true
|
|
72
|
+
```bash
|
|
73
|
+
uv run {forgeTierRwHelper} init-prefs \
|
|
74
|
+
--target "{project-root}/_bmad/_memory/forger-sidecar/preferences.yaml"
|
|
75
|
+
```
|
|
84
76
|
|
|
85
|
-
|
|
86
|
-
headless_mode: false
|
|
77
|
+
The script creates the file with first-run defaults (matching the prior inline template — `tier_override: ~`, `passive_context: true`, `headless_mode: false`, `compact_greeting: false`, plus reserved-for-future-use commented fields) IF the file does not exist. When the file already exists, the script refuses to overwrite (preserves user customization) and reports `wrote: false`.
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
compact_greeting: false
|
|
79
|
+
**Parse the response and set context flags for step-04:**
|
|
90
80
|
|
|
91
|
-
|
|
92
|
-
# output_language: ~
|
|
93
|
-
# skill_format_version: ~
|
|
94
|
-
# citation_style: ~
|
|
95
|
-
# confidence_display: ~
|
|
96
|
-
```
|
|
81
|
+
- `{preferences_yaml_created}` ← `wrote` (true on first run, false on re-run when the file pre-existed)
|
|
97
82
|
|
|
98
|
-
**If
|
|
83
|
+
**If the script exits non-zero**: same halt-and-report pattern as section 1, with `phase: "step-02:init-prefs"` and the matching path.
|
|
99
84
|
|
|
100
85
|
### 3. Ensure forge-data/ Directory
|
|
101
86
|
|
|
102
87
|
Check if `{forge_data_folder}` directory exists:
|
|
103
88
|
|
|
104
|
-
- If missing: create it
|
|
105
|
-
- If exists: skip silently
|
|
89
|
+
- If missing: create it. Store `{forge_data_dir_created: true}` in context.
|
|
90
|
+
- If exists: skip silently. Store `{forge_data_dir_created: false}` in context.
|
|
106
91
|
|
|
107
|
-
|
|
92
|
+
If the create fails (parent unwritable, disk full, permission denied), set `{error: {phase: "step-02:forge-data-dir", path: "{forge_data_folder}", reason: <error message>}}` for step-04's envelope and halt the workflow.
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
#### Menu Handling Logic:
|
|
112
|
-
|
|
113
|
-
- After all file operations complete successfully, immediately load, read entire file, then execute {nextStepFile}
|
|
114
|
-
|
|
115
|
-
#### EXECUTION RULES:
|
|
116
|
-
|
|
117
|
-
- This is an auto-proceed step with no user choices
|
|
118
|
-
- Proceed directly to next step after configuration is written
|
|
119
|
-
|
|
120
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
121
|
-
|
|
122
|
-
ONLY WHEN forge-tier.yaml has been written successfully and preferences.yaml exists (created or pre-existing) will you load and read fully `{nextStepFile}` to execute the QMD hygiene step.
|
|
94
|
+
### 4. Auto-Proceed
|
|
123
95
|
|
|
96
|
+
After forge-tier.yaml has been written successfully and preferences.yaml exists (created or pre-existing), display "**Proceeding to QMD collection hygiene...**", then load `{nextStepFile}`, read it fully, and execute it.
|
|
@@ -1,73 +1,89 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: './step-04-report.md'
|
|
3
|
+
# Resolve `{qmdClassifyHelper}` and `{forgeTierRwHelper}` by probing the
|
|
4
|
+
# corresponding `*ProbeOrder` arrays (installed SKF module path first, src/
|
|
5
|
+
# dev-checkout fallback); first existing path wins. HALT if neither resolves
|
|
6
|
+
# for the helper a section actually invokes — both scripts own classification
|
|
7
|
+
# / registry-cleanup contracts that have no prose fallback.
|
|
8
|
+
qmdClassifyProbeOrder:
|
|
9
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-qmd-classify-collections.py'
|
|
10
|
+
- '{project-root}/src/shared/scripts/skf-qmd-classify-collections.py'
|
|
11
|
+
forgeTierRwProbeOrder:
|
|
12
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
|
|
13
|
+
- '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
|
|
3
14
|
---
|
|
4
15
|
|
|
5
|
-
|
|
16
|
+
<!-- Config: communicate in {communication_language}. The orphan-removal prompt and the headless-resolution log message render in the user's language. -->
|
|
17
|
+
|
|
18
|
+
# Step 3: QMD + CCC Registry Hygiene
|
|
6
19
|
|
|
7
20
|
## STEP GOAL:
|
|
8
21
|
|
|
9
|
-
|
|
22
|
+
When the detected tier is Deep, classify live QMD collections against the `qmd_collections` registry and prompt the user before removing orphans. Whenever ccc is available (Forge+ or Deep), prune `ccc_index_registry` entries whose source paths no longer exist. All set-arithmetic and YAML mutation goes through scripts (`{qmdClassifyHelper}` and `{forgeTierRwHelper}`); the workflow only orchestrates external CLI calls (`qmd collection list`, `qmd collection remove`) and the user-prompt branch.
|
|
10
23
|
|
|
11
|
-
For Quick and Forge tiers, skip silently and proceed (QMD is not available
|
|
24
|
+
For Quick and Forge tiers, skip silently and proceed (QMD is not available; ccc registry cleanup only runs when ccc is available regardless of tier).
|
|
12
25
|
|
|
13
26
|
## Rules
|
|
14
27
|
|
|
15
|
-
- Focus only on
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- Do not
|
|
28
|
+
- Focus only on registry hygiene — no new collection creation (that belongs to create-skill)
|
|
29
|
+
- Never reimplement the forge-namespace suffix filter in prose — the classifier owns it
|
|
30
|
+
- Never silently delete collections — always prompt before `qmd collection remove`
|
|
31
|
+
- Headless runs must auto-resolve the orphan prompt to the documented default (Keep)
|
|
32
|
+
- Do not fail the workflow if hygiene encounters errors
|
|
20
33
|
|
|
21
34
|
## MANDATORY SEQUENCE
|
|
22
35
|
|
|
23
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
24
|
-
|
|
25
36
|
### 1. Check Tier
|
|
26
37
|
|
|
27
|
-
Read `{calculated_tier}` from context.
|
|
38
|
+
Read `{calculated_tier}` and `{ccc}` from context (set by step-01).
|
|
28
39
|
|
|
29
|
-
**If
|
|
40
|
+
**If `{calculated_tier}` is Quick or Forge AND `{ccc}` is false:** No registry hygiene needed. Set `{hygiene_result: "skipped", hygiene_healthy: 0, hygiene_orphaned_removed: 0, hygiene_orphaned_kept: 0, hygiene_stale_cleaned: 0, ccc_registry_stale_cleaned: 0, ccc_registry_stale_removed_paths: []}`. Proceed directly to section 5 (Auto-Proceed) — no output, no messaging.
|
|
30
41
|
|
|
31
|
-
**If
|
|
42
|
+
**If `{calculated_tier}` is Quick or Forge AND `{ccc}` is true:** No QMD work, but ccc registry needs pruning. Set QMD-related flags to defaults (`hygiene_result: "skipped"`, all hygiene_* counts = 0). Skip directly to section 4 (Stale Registry Cleanup), running it with the ccc-prune flag only.
|
|
32
43
|
|
|
33
|
-
**If
|
|
44
|
+
**If `{calculated_tier}` is Forge+:** Same as Quick/Forge with ccc — no QMD work (qmd unavailable at Forge+), but ccc registry hygiene runs.
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
**If `{calculated_tier}` IS Deep:** Continue to section 2.
|
|
36
47
|
|
|
37
|
-
|
|
48
|
+
### 2. Classify Live QMD Collections vs Registry
|
|
38
49
|
|
|
39
50
|
List live QMD collections:
|
|
51
|
+
|
|
40
52
|
```bash
|
|
41
53
|
qmd collection list
|
|
42
54
|
```
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
Parse the output into a comma-separated string of collection names and store as `{live_collections}` (raw — including any foreign collections owned by other tools sharing the QMD daemon; the classifier filters them out).
|
|
57
|
+
|
|
58
|
+
**Error handling:** If `qmd collection list` fails (daemon down, daemon errors), set `{hygiene_result: "qmd_unavailable", hygiene_healthy: 0, hygiene_orphaned_removed: 0, hygiene_orphaned_kept: 0, hygiene_stale_cleaned: 0}`, log the error, and skip directly to section 4 (which will still run the ccc-prune branch if `{ccc}` is true).
|
|
47
59
|
|
|
48
|
-
|
|
60
|
+
Run the classifier. Invoke via `uv run` so the script's PEP 723 PyYAML dependency resolves automatically (`docs/getting-started.md` documents uv as the runtime prereq for exactly this); bare `python3` would `ModuleNotFoundError` on a fresh interpreter.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uv run {qmdClassifyHelper} \
|
|
64
|
+
--live-names "{live_collections}" \
|
|
65
|
+
--registry-from-yaml "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml"
|
|
66
|
+
```
|
|
49
67
|
|
|
50
|
-
|
|
68
|
+
The script (see `src/shared/scripts/skf-qmd-classify-collections.py` docstring for the full schema) applies the forge-namespace suffix filter (`-brief | -temporal | -docs | -extraction`) to `{live_collections}` before classifying — collections owned by unrelated tools are silently excluded from the orphan / healthy / stale sets and counted under `foreign_filtered_count` for telemetry only. This is the PR #244 incident protection: collections like Hindsight memory banks that happen to live in the same QMD daemon never enter any classification that could lead to data loss.
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
**Parse the JSON output and set context flags:**
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
72
|
+
- `{hygiene_healthy}` ← `len(healthy)`
|
|
73
|
+
- `{orphaned_collections}` ← `orphaned` (the list — used in section 3)
|
|
74
|
+
- `{stale_collections}` ← `stale` (the list — used in section 4)
|
|
75
|
+
- `{foreign_filtered_count}` ← `foreign_filtered_count`
|
|
57
76
|
|
|
58
|
-
|
|
59
|
-
- These may be leftover from prior auto-indexing or manual indexing
|
|
60
|
-
- Flag for user-prompted removal
|
|
77
|
+
Set `{hygiene_result: "completed"}`.
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
- The QMD collection was deleted or lost
|
|
64
|
-
- Remove the stale entry from the registry
|
|
79
|
+
### 3. Handle Orphaned Collections
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
**If `{orphaned_collections}` is empty:** Set `{hygiene_orphaned_removed: 0, hygiene_orphaned_kept: 0}` and skip to section 4.
|
|
67
82
|
|
|
68
|
-
**If orphaned
|
|
83
|
+
**Headless gate.** If `{headless_mode}` is true, auto-resolve to the default action **Keep** without prompting: log `"Auto-decision (headless): kept {len(orphaned_collections)} orphaned forge collection(s)"`, set `{hygiene_orphaned_removed: 0, hygiene_orphaned_kept: len(orphaned_collections)}`, and skip to section 4. This matches the workflow contract (`Headless: All gates auto-resolve with default action when {headless_mode} is true`) declared in the Invocation Contract.
|
|
84
|
+
|
|
85
|
+
**If `{headless_mode}` is false**, display to the user:
|
|
69
86
|
|
|
70
|
-
Display to user:
|
|
71
87
|
"**QMD Hygiene: Found {count} orphaned collection(s) not tracked in the forge registry:**
|
|
72
88
|
|
|
73
89
|
{list orphaned collection names}
|
|
@@ -75,77 +91,43 @@ Display to user:
|
|
|
75
91
|
These collections exist in QMD but are not managed by any skill workflow. They may be from a previous auto-index run or manual creation.
|
|
76
92
|
|
|
77
93
|
**[R]emove** orphaned collections — clean up QMD
|
|
78
|
-
**[K]eep** orphaned collections — leave them as-is"
|
|
94
|
+
**[K]eep** orphaned collections — leave them as-is (default)"
|
|
95
|
+
|
|
96
|
+
**If user selects R (Remove):** For each name in `{orphaned_collections}`:
|
|
79
97
|
|
|
80
|
-
**If user selects R (Remove):**
|
|
81
|
-
For each orphaned collection:
|
|
82
98
|
```bash
|
|
83
|
-
qmd collection remove
|
|
99
|
+
qmd collection remove <name>
|
|
84
100
|
```
|
|
85
|
-
Log each removal.
|
|
86
|
-
|
|
87
|
-
**If user selects K (Keep):**
|
|
88
|
-
Skip removal. Log that orphaned collections were kept.
|
|
89
|
-
|
|
90
|
-
**If no orphaned collections:** Skip this section silently.
|
|
91
|
-
|
|
92
|
-
### 5. Handle Stale Registry Entries
|
|
93
|
-
|
|
94
|
-
**If stale registry entries found:**
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
Track the count of successful removals as `{hygiene_orphaned_removed}`. Set `{hygiene_orphaned_kept: 0}`.
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
**If user selects K (Keep) or no orphans:** Set `{hygiene_orphaned_removed: 0, hygiene_orphaned_kept: len(orphaned_collections)}`.
|
|
99
105
|
|
|
100
|
-
|
|
106
|
+
### 4. Stale Registry Cleanup
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
This section ALWAYS runs when reachable — it handles both `qmd_collections` stale entries (Deep tier) and `ccc_index_registry` stale entries (whenever ccc is true). The script's flags are mutually independent.
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
Build the invocation. Always include `--target` for the forge-tier.yaml path. Include `--qmd-live-names "{live_collections}"` ONLY when section 2 ran successfully (i.e. `{hygiene_result}` is `"completed"`); omit the flag entirely otherwise so the script skips QMD cleanup. Include `--prune-missing-ccc-paths` ONLY when `{ccc}` is true; omit it otherwise.
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
**Healthy** — path exists: no action needed.
|
|
113
|
-
|
|
114
|
-
**Stale** — path does not exist (source directory removed or moved):
|
|
115
|
-
- Remove the stale entry from `ccc_index_registry`
|
|
116
|
-
- Log: "Removed stale CCC index registry entry: {path} (path no longer exists)"
|
|
117
|
-
|
|
118
|
-
Update forge-tier.yaml with the cleaned registry.
|
|
119
|
-
|
|
120
|
-
Store `{ccc_registry_stale_cleaned: count}` in context for step-04 reporting.
|
|
121
|
-
|
|
122
|
-
**IF `tools.ccc` is false:** Skip this section silently.
|
|
123
|
-
|
|
124
|
-
### 6. Store Hygiene Results and Auto-Proceed
|
|
125
|
-
|
|
126
|
-
Store in context for step-04 reporting:
|
|
127
|
-
```
|
|
128
|
-
{hygiene_result: "completed"|"skipped"|"qmd_unavailable"}
|
|
129
|
-
{hygiene_healthy: count}
|
|
130
|
-
{hygiene_orphaned_removed: count}
|
|
131
|
-
{hygiene_orphaned_kept: count}
|
|
132
|
-
{hygiene_stale_cleaned: count}
|
|
133
|
-
{ccc_registry_stale_cleaned: count}
|
|
112
|
+
```bash
|
|
113
|
+
uv run {forgeTierRwHelper} clean-stale \
|
|
114
|
+
--target "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml" \
|
|
115
|
+
[--qmd-live-names "{live_collections}"] \
|
|
116
|
+
[--prune-missing-ccc-paths]
|
|
134
117
|
```
|
|
135
118
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
#### Menu Handling Logic:
|
|
119
|
+
The script reads the registry, computes set-difference operations (qmd: registry − live; ccc: filter where `path` does not exist on disk), and atomically rewrites forge-tier.yaml only when something actually changed (mtime preserved on idempotent re-runs). The CI ephemeral-mount caveat for ccc-registry pruning is logged in the script's WARNING message (per PR #248).
|
|
139
120
|
|
|
140
|
-
|
|
121
|
+
**Parse the JSON output and set context flags for step-04:**
|
|
141
122
|
|
|
142
|
-
|
|
123
|
+
- `{hygiene_stale_cleaned}` ← `len(qmd_removed)`
|
|
124
|
+
- `{ccc_registry_stale_cleaned}` ← `len(ccc_removed)`
|
|
125
|
+
- `{ccc_registry_stale_removed_paths}` ← `ccc_removed` (the list — step-04 folds individual paths into envelope warnings)
|
|
143
126
|
|
|
144
|
-
|
|
145
|
-
- If no orphans found, this is an auto-proceed step
|
|
146
|
-
- Proceed directly to next step after hygiene or skip
|
|
127
|
+
If `{hygiene_stale_cleaned}` > 0, display: "**Cleaned {hygiene_stale_cleaned} stale QMD registry entry/entries** (collection no longer exists in QMD)."
|
|
147
128
|
|
|
148
|
-
|
|
129
|
+
(The script already logs each ccc removal as a WARNING line; no additional display needed.)
|
|
149
130
|
|
|
150
|
-
|
|
131
|
+
### 5. Auto-Proceed
|
|
151
132
|
|
|
133
|
+
After hygiene completes (or is skipped for non-Deep tiers without ccc), display "**Proceeding to forge status report...**", then load `{nextStepFile}`, read it fully, and execute it.
|