bmad-module-skill-forge 5.0.0 → 6.0.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/docs/_data/pinned.yaml +1 -1
- package/docs/agents.md +1 -1
- package/docs/architecture.md +3 -3
- package/docs/campaign.md +1 -1
- package/docs/troubleshooting.md +9 -3
- package/docs/verifying-a-skill.md +9 -9
- package/docs/workflows.md +4 -4
- package/package.json +1 -1
- package/src/knowledge/tool-resolution.md +6 -6
- package/src/shared/references/headless-gate-convention.md +4 -1
- package/src/shared/references/output-contract-schema.md +3 -3
- package/src/shared/references/pipeline-contracts.md +2 -1
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +1 -1
- package/src/shared/scripts/skf-build-change-manifest.py +332 -54
- package/src/shared/scripts/skf-classify-changed-files.py +15 -10
- package/src/shared/scripts/skf-emit-result-envelope.py +58 -15
- package/src/shared/scripts/skf-extract-public-api.py +33 -13
- package/src/shared/scripts/skf-parse-gaps.py +3 -2
- package/src/shared/scripts/skf-verify-provenance-completeness.py +108 -11
- package/src/skf-analyze-source/SKILL.md +1 -0
- package/src/skf-analyze-source/references/generate-briefs.md +1 -1
- package/src/skf-analyze-source/references/headless-contract.md +1 -1
- package/src/skf-audit-skill/SKILL.md +2 -1
- package/src/skf-audit-skill/references/report.md +1 -1
- package/src/skf-brief-skill/SKILL.md +1 -0
- package/src/skf-brief-skill/references/invocation-contract.md +2 -2
- package/src/skf-campaign/SKILL.md +1 -0
- package/src/skf-campaign/references/campaign-contracts.md +2 -0
- package/src/skf-campaign/references/step-05-skill-loop.md +2 -2
- package/src/skf-campaign/references/step-10-export.md +3 -2
- package/src/skf-create-skill/SKILL.md +2 -1
- package/src/skf-create-skill/references/ccc-index-check.md +1 -1
- package/src/skf-create-skill/references/component-extraction.md +1 -1
- package/src/skf-create-skill/references/extract.md +1 -1
- package/src/skf-create-skill/references/generate-artifacts.md +1 -1
- package/src/skf-create-skill/references/report.md +2 -2
- package/src/skf-create-skill/references/sub/ccc-discover.md +1 -1
- package/src/skf-create-skill/references/validate.md +1 -1
- package/src/skf-create-stack-skill/SKILL.md +1 -0
- package/src/skf-create-stack-skill/references/generate-output.md +1 -1
- package/src/skf-create-stack-skill/references/invocation-contract.md +1 -1
- package/src/skf-create-stack-skill/references/parallel-extract.md +2 -2
- package/src/skf-create-stack-skill/references/report.md +1 -1
- package/src/skf-drop-skill/SKILL.md +1 -0
- package/src/skf-drop-skill/references/invocation-contract.md +1 -1
- package/src/skf-drop-skill/references/report.md +1 -1
- package/src/skf-export-skill/SKILL.md +1 -0
- package/src/skf-export-skill/references/invocation-contract.md +1 -1
- package/src/skf-export-skill/references/summary.md +1 -1
- package/src/skf-forger/SKILL.md +1 -0
- package/src/skf-forger/references/pipeline-mode.md +2 -2
- package/src/skf-quick-skill/SKILL.md +2 -1
- package/src/skf-quick-skill/references/finalize.md +1 -1
- package/src/skf-quick-skill/references/halt-contract.md +2 -2
- package/src/skf-quick-skill/references/write-and-validate.md +1 -1
- package/src/skf-refine-architecture/SKILL.md +2 -1
- package/src/skf-refine-architecture/customize.toml +1 -1
- package/src/skf-refine-architecture/references/report.md +1 -1
- package/src/skf-rename-skill/SKILL.md +1 -0
- package/src/skf-rename-skill/references/invocation-contract.md +2 -2
- package/src/skf-setup/SKILL.md +1 -0
- package/src/skf-setup/references/ccc-index.md +1 -1
- package/src/skf-setup/references/invocation-contract.md +1 -1
- package/src/skf-test-skill/SKILL.md +1 -0
- package/src/skf-test-skill/assets/output-section-formats.md +4 -4
- package/src/skf-test-skill/references/coherence-check.md +8 -6
- package/src/skf-test-skill/references/coverage-check-branches.md +23 -3
- package/src/skf-test-skill/references/coverage-check-tiers.md +3 -3
- package/src/skf-test-skill/references/coverage-check.md +30 -43
- package/src/skf-test-skill/references/init.md +1 -1
- package/src/skf-test-skill/references/inventory-contract.md +27 -0
- package/src/skf-test-skill/references/invocation-contract.md +3 -3
- package/src/skf-test-skill/references/report.md +3 -3
- package/src/skf-test-skill/references/scoring-rules.md +6 -6
- package/src/skf-test-skill/references/signature-comparison-rules.md +72 -0
- package/src/skf-test-skill/references/source-access-protocol.md +12 -8
- package/src/skf-test-skill/references/step-hard-gate.md +1 -0
- package/src/skf-test-skill/scripts/build-result-context.py +20 -3
- package/src/skf-test-skill/scripts/gap-ledger.py +78 -13
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +345 -9
- package/src/skf-test-skill/scripts/locate-export-segments.py +591 -63
- package/src/skf-test-skill/scripts/score-signatures.py +939 -74
- package/src/skf-test-skill/scripts/validate-inventory.py +245 -29
- package/src/skf-update-skill/SKILL.md +1 -0
- package/src/skf-update-skill/references/gap-driven.md +28 -22
- package/src/skf-update-skill/references/invocation-contract.md +1 -1
- package/src/skf-update-skill/references/merge.md +4 -4
- package/src/skf-update-skill/references/report.md +1 -1
- package/src/skf-update-skill/references/write.md +5 -4
- package/src/skf-verify-stack/SKILL.md +2 -1
package/docs/_data/pinned.yaml
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
# Enforced two ways: (1) release.yaml bumps this line in the same commit
|
|
33
33
|
# as package.json + marketplace.json on every release; (2) validate-docs-drift.js
|
|
34
34
|
# cross-checks this value against package.json.version and fails on mismatch.
|
|
35
|
-
skf_version: "
|
|
35
|
+
skf_version: "6.0.0"
|
|
36
36
|
|
|
37
37
|
# Path to the oh-my-skills repo, resolved relative to this repo's root.
|
|
38
38
|
# Override at runtime with the OMS environment variable if oh-my-skills
|
package/docs/agents.md
CHANGED
|
@@ -77,4 +77,4 @@ Ferris can run several workflows in one command. Type their codes in order, such
|
|
|
77
77
|
`campaign` is not a chaining alias. It is a separate workflow (`@Ferris CA` or `@Ferris campaign`) that runs its own pipeline for many skills, builds them in the order their dependencies need, and can resume where it stopped. `CA` cannot be chained with other codes. See the [Campaign](/docs/campaign.md) page.
|
|
78
78
|
|
|
79
79
|
**Memory:**
|
|
80
|
-
Ferris keeps a memory folder, called the sidecar, at `_bmad/_memory/forger-sidecar/`, so he remembers your setup between sessions. It holds `forge-tier.yaml` (the tools Setup Forge found and your capability tier), `preferences.yaml` (your settings) and the result of
|
|
80
|
+
Ferris keeps a memory folder, called the sidecar, at `_bmad/_memory/forger-sidecar/`, so he remembers your setup between sessions. It holds `forge-tier.yaml` (the tools Setup Forge found and your capability tier), `preferences.yaml` (your settings) and the result of the last pipeline run that reached its end (`pipeline-result-latest.json`; a chain cut off before it ends writes none, so the file can be an earlier chain's). A pipeline keeps its progress in a journal in its own run folder under `_bmad-output/.skf-run/`. If one stopped partway, Ferris reads that journal the next time you open him and offers to resume it, or to repair it first after a failed test, and you can tell him to drop the offer. A start with `--headless` (or `-H`) that names no code, alias or chain stops with "Nothing to run". In `preferences.yaml`, set `headless_mode: true` to make headless the default, or `compact_greeting: true` to get a short greeting without the full menu table (ask for the menu when you want it). [Getting Started → Configuration](/docs/getting-started.md#configuration) lists more preferences.
|
package/docs/architecture.md
CHANGED
|
@@ -57,7 +57,7 @@ flowchart LR
|
|
|
57
57
|
2. **Agent loads**: `skf-forger/SKILL.md` puts the persona (identity, principles, critical actions) into the context window. Ferris also loads its sidecar files (`forge-tier.yaml`, `preferences.yaml`), which keep your tier and preferences between sessions.
|
|
58
58
|
3. **Workflow loads**: The workflow's `SKILL.md` loads your SKF settings from `_bmad/skf/config.yaml` and checks whether the run is headless (runs without asking questions). It then runs SKF's customization helper through `uv`, which runs BMAD Method's customization script to apply any team or personal overrides to the workflow's `customize.toml`, as Ferris does for his own settings when he starts. When that script cannot run (with SKF installed alone it is missing), the workflow prints one warning, adds `customization_resolver_unavailable` to the run's warnings (an interactive Export Skill run only prints it) and keeps the bundled settings. Then it opens the first step file.
|
|
59
59
|
4. **Step-by-step execution**: Only the current step file is in context (just-in-time loading). Each step names the next one. The assistant reads the step, carries it out, saves its output, then loads the next step. Future steps are never loaded early.
|
|
60
|
-
5. **Sub-agent delegation**: Some steps would otherwise read many large files, such as several `SKILL.md` documents or every file that imports a library. These steps hand the reading to sub-agents, which are helper AI sessions started with the Agent tool. Each sub-agent reads its share and returns a short JSON summary. The parent never loads the full text, so the next step starts with an uncluttered context window. AN (per-unit analysis), TS (coverage and coherence checks), VS (integration verification), RA (gap analysis) and SS (per-library extraction) work this way. VS and RA run at most 8 sub-agents at a time. SS runs 3 to 5 at a time in Claude Code.
|
|
60
|
+
5. **Sub-agent delegation**: Some steps would otherwise read many large files, such as several `SKILL.md` documents or every file that imports a library. These steps hand the reading to sub-agents, which are helper AI sessions started with the Agent tool. Each sub-agent reads its share and returns a short JSON summary, and the step waits in the foreground until every one has returned, since a headless `claude -p` run stops when the agent's turn ends. The parent never loads the full text, so the next step starts with an uncluttered context window. AN (per-unit analysis), TS (coverage and coherence checks), VS (integration verification), RA (gap analysis) and SS (per-library extraction) work this way. VS and RA run at most 8 sub-agents at a time. SS runs 3 to 5 at a time in Claude Code.
|
|
61
61
|
6. **Knowledge injection**: Steps consult `skf-knowledge-index.csv` and load only the fragments from `knowledge/` that match by tags and relevance. Cross-cutting principles (zero hallucination, confidence tiers, provenance) load only when a step asks for them, never in advance.
|
|
62
62
|
7. **Reference and asset injection**: Steps read `references/*.md` and `assets/*.md` files as needed (rules, patterns, schemas, heuristics). This is deliberate: only the data the current step needs enters the context window.
|
|
63
63
|
8. **Script execution**: Steps run deterministic Python scripts (`scripts/*.py`, `shared/scripts/*.py`), usually with `uv run`, for work that must be reproducible: scoring, structural diffing, manifest operations, frontmatter validation. The assistant prepares the inputs, the script computes, and the assistant uses the output. The same inputs always give the same result.
|
|
@@ -143,14 +143,14 @@ forge-data/{skill-name}/
|
|
|
143
143
|
├── test-findings-{run_id}.json # Test Skill's gap ledger, which its hard gate reads
|
|
144
144
|
├── drift-report-{timestamp}.md # Written by Audit Skill
|
|
145
145
|
├── .skf-audit/{timestamp}/ # The JSON each audit scored its drift from
|
|
146
|
-
└── {workflow}-result-latest.json # Machine-readable result of the last run
|
|
146
|
+
└── {workflow}-result-latest.json # Machine-readable result of the last run whose emitter wrote this copy
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
The `provenance-map.json` includes per-export `entries` with a `source_library` field naming the library each export belongs to. For stack skills, it also includes an `integrations` array (cross-library patterns) and, in compose mode only, a `constituents` array. That array records a fingerprint (hash) of each source skill's `metadata.json` at compose time, so Audit Skill can tell when a source skill has changed since. The `file_entries` array tracks copied scripts and assets file by file (SHA-256 hashes, source paths).
|
|
150
150
|
|
|
151
151
|
### Pipeline Result Contracts
|
|
152
152
|
|
|
153
|
-
Pipeline-facing workflows write a machine-readable result JSON file alongside their human-readable output. This makes CI checks and chained runs reliable: a later workflow or script can check what the earlier step produced without parsing Markdown. Each run writes two files. The first is a timestamped record, `{workflow}-result-{YYYYMMDD-HHmmss}.json`, which keeps the full history across retries and aborts. The second is a copy named `{workflow}-result-latest.json`, which other tools read without sorting through timestamps. `{workflow}` names the workflow, not the skill being built. Most workflows leave off the `skf-` prefix (`create-skill`, `update-skill`), but Test Skill keeps it. Its record is `skf-test-skill-result-{YYYYMMDD-HHmmss}.json` (UTC, with `-2` appended when a run already took that second's name), no longer named after the run ID. The files sit next to what the run produced. For Create, Update, Test and Audit Skill, that is the skill's version folder in `forge-data/`. When Ferris runs a chain of workflows, he keeps its state in a journal, `pipeline-journal.json`, in the chain's run folder under `_bmad-output/.skf-run/`, and when the chain ends he writes its result from that journal: `pipeline-result-<YYYYMMDD-HHmmss>.json` and its `pipeline-result-latest.json` copy in his sidecar folder. The schema follows a consistent format: `skill`, `status` (success/failed/partial), `timestamp`, `outputs` (array of produced artifacts with type and path), and a skill-specific `summary` object.
|
|
153
|
+
Pipeline-facing workflows write a machine-readable result JSON file alongside their human-readable output. This makes CI checks and chained runs reliable: a later workflow or script can check what the earlier step produced without parsing Markdown. Each run writes two files. The first is a timestamped record, `{workflow}-result-{YYYYMMDD-HHmmss}.json`, which keeps the full history across retries and aborts. The second is a copy named `{workflow}-result-latest.json`, the record of the last run whose emitter wrote this copy, which other tools read without sorting through timestamps. A caller that started a run reads that run's own result line instead: a run cut off before its emitter call printed none and wrote no record, so the copy then belongs to an earlier run. `{workflow}` names the workflow, not the skill being built. Most workflows leave off the `skf-` prefix (`create-skill`, `update-skill`), but Test Skill keeps it. Its record is `skf-test-skill-result-{YYYYMMDD-HHmmss}.json` (UTC, with `-2` appended when a run already took that second's name), no longer named after the run ID. The files sit next to what the run produced. For Create, Update, Test and Audit Skill, that is the skill's version folder in `forge-data/`. When Ferris runs a chain of workflows, he keeps its state in a journal, `pipeline-journal.json`, in the chain's run folder under `_bmad-output/.skf-run/`, and when the chain ends he writes its result from that journal: `pipeline-result-<YYYYMMDD-HHmmss>.json` and its `pipeline-result-latest.json` copy in his sidecar folder. A chain cut off before it ends writes none, so the copy can be an earlier chain's. The schema follows a consistent format: `skill`, `status` (success/failed/partial), `timestamp`, `outputs` (array of produced artifacts with type and path), and a skill-specific `summary` object.
|
|
154
154
|
|
|
155
155
|
`skills/` and `forge-data/` are committed. Agent memory (`_bmad/_memory/forger-sidecar/`) is gitignored.
|
|
156
156
|
|
package/docs/campaign.md
CHANGED
|
@@ -186,7 +186,7 @@ A finished campaign leaves:
|
|
|
186
186
|
- **Verification report and refined architecture**: from Verification and Refinement, when an architecture document was found.
|
|
187
187
|
- **Campaign report** (`campaign-report.md`): the quality gate; a table of every skill with its tier, status, score, pin and workarounds; an Export Gate section with each completed skill's verdict, whether Export exported it or why its export failed, and the skills the gate kept from export; the lowest, highest and average scores; time per skill; and a section on failed or skipped skills.
|
|
188
188
|
- **Decision log** (`_campaign-decision-log.md`): every decision made during the campaign.
|
|
189
|
-
- **Headless result line** (`SKF_CAMPAIGN_RESULT_JSON`): printed in headless mode for scripts to read, as the run's last line. Besides the counts and the score of each skill, it carries `export_verdicts` (each completed skill's verdict at Export), `skills_excluded` (the completed skills the gate kept from export), `skills_exported` (the skills Export exported) and `export_failures` (each skill whose export failed, with its `halt_reason` and `exit_code`). The last two are null when the Export stage did not run. A failed export does not fail the campaign, which still ends with `status` `success`, so a CI job that needs every skill exported checks `export_failures`.
|
|
189
|
+
- **Headless result line** (`SKF_CAMPAIGN_RESULT_JSON`): printed in headless mode for scripts to read, as the run's last line. Besides the counts and the score of each skill, it carries `export_verdicts` (each completed skill's verdict at Export), `skills_excluded` (the completed skills the gate kept from export), `skills_exported` (the skills Export exported) and `export_failures` (each skill whose export failed, with its `halt_reason` and `exit_code`). The last two are null when the Export stage did not run. An `export_failures` entry's `exit_code` is null when the export printed no line (its `halt_reason` is then `no-result`) or one Campaign could not read (`result-unreadable`). A failed export does not fail the campaign, which still ends with `status` `success`, so a CI job that needs every skill exported checks `export_failures`.
|
|
190
190
|
|
|
191
191
|
The Export stage (stage 9) asks before it writes anything. It lists each completed skill with its verdict, names the skills it leaves out because they fall below the soft fallback or have no score, and waits for `[E]xport all` or `[C]ancel`. `[E]xport all` exports only the `pass` and `fallback` skills, each under the skill folder its build wrote: a Tier B skill under the name Quick Skill gave it, the library's name, even when you named the target differently. In headless mode it exports them without asking.
|
|
192
192
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -53,7 +53,7 @@ When ccc is available (Forge+, or Deep with ccc), create-skill indexes the skill
|
|
|
53
53
|
|
|
54
54
|
Surfaced by `@Ferris SF` when ccc is available and `ccc index` exits with an error: the forge status shows `indexing failed, semantic discovery unavailable this session` with ccc's own message. Setup still finishes at your tier; only semantic discovery is off until the index builds. One cause is an index that an older ccc wrote and the installed ccc cannot read, with a message such as `Failed to deserialize pickle payload`.
|
|
55
55
|
|
|
56
|
-
**Fix:** from the project root, run `ccc reset`, then `ccc daemon restart`, then re-run `@Ferris SF`. `ccc reset` deletes only the index databases and keeps `.cocoindex_code/settings.yml` with SKF's exclusions. Restart the daemon before indexing again: it still holds the databases `ccc reset` deleted, so an index built right after the reset can fail with `environment already open in this program`.
|
|
56
|
+
**Fix:** from the project root, run `ccc reset`, then `ccc daemon restart`, then re-run `@Ferris SF`. `ccc reset` deletes only the index databases and keeps `.cocoindex_code/settings.yml` with SKF's exclusions. Restart the daemon before indexing again: it still holds the databases `ccc reset` deleted, so an index built right after the reset can fail with `environment already open in this program`. In a headless run, a first index of a large project can also outlast the longest timeout the agent's shell gives a command, and Setup then reports the stopped call as a failed index (an interactive run builds such an index in the background and waits for it): run `ccc index` in a terminal in the project, then re-run `@Ferris SF`.
|
|
57
57
|
|
|
58
58
|
### `@Ferris deepwiki` shows a deprecation notice
|
|
59
59
|
|
|
@@ -69,6 +69,12 @@ Test Skill checks a skill against the source commit it was made from. When the s
|
|
|
69
69
|
|
|
70
70
|
**Fix:** check out the pinned commit the message shows with `git -C <source> checkout <pinned commit>`, then re-run `@Ferris TS <name>`. The checkout line in the message names the skill's `source_ref` when it has one. When `source_ref` is a branch, that line does not move a checkout that is already on a newer commit of the branch. To test against the source as it is now, re-run with `--allow-workspace-drift`: the findings then describe the current checkout, and a pass under that flag does not clear the skill for export until you re-test at the pinned commit. Right after an update, follow the update's report instead (see "Update Skill stops with `blocked` before detecting changes").
|
|
71
71
|
|
|
72
|
+
### Test Skill stops with `another-run-active`
|
|
73
|
+
|
|
74
|
+
Surfaced by `@Ferris TS` (exit `1`) at one of two points. At the start (under `--headless`, `error.phase` is `init:run-lock`), another test of the same skill version holds its run lock, `.test-skill.lock` in `<forge_data_folder>/<name>/<version>/`, and the lock has not gone stale (60 minutes after it was taken or renewed). Just before it writes its results (`report:run-lock`), the run's own lock went stale and another run took it over, so it stops and writes no result. A run that was cut off, by a closed session or by a `claude -p` run whose agent ended its turn early (no `SKF_TEST_RESULT_JSON` line, and `claude -p` still exits `0`), leaves its lock behind, with its in-progress report `.skf-test-report-<name>-<run_id>.md` in the version folder and its run folder `_bmad-output/.skf-run/skf-test-skill-<run_id>/`.
|
|
75
|
+
|
|
76
|
+
**Fix:** wait until the other test ends, then re-run. If no test of that skill is running, delete the lock file the message names, or wait until the time it gives: a test that finds a stale lock takes it over. After `report:run-lock`, re-run once the run that took the lock over has ended. A run cut off before its result step wrote no result, so `skf-test-skill-result-latest.json` still holds an earlier run's verdict, and one cut off later wrote it: re-run the test rather than read that file. Its in-progress report and run folder are safe to delete once no test of that skill is running.
|
|
77
|
+
|
|
72
78
|
### forge-auto halted at the Test stage
|
|
73
79
|
|
|
74
80
|
forge-auto aims for 90%, stricter than the default 80%. A score of at least 80% but under 90% still passes, with an `evidence-report-fallback.md` that records the shortfall, unless a cap fired. TS stops the pipeline on any verdict but PASS: every FAIL (a score below 80%, a Critical or High gap at the hard gate, or a cap that turned a pass into a fail whatever the score), INCONCLUSIVE, and pass-with-drift. Run `@Ferris US <name> --from-test-report` to repair the gaps the report lists, then `@Ferris TS[<name>] EX` to test and export again (the brackets give Test Skill the skill's name). A plain `@Ferris US <name>` finds a failed or pass-with-drift test report that is newer than the skill and that no repair has applied, and offers to repair its gaps; a headless run checks the source for changes instead and adds an `unconsumed-test-report` warning. For INCONCLUSIVE, add evidence as [Pass/fail](/docs/verifying-a-skill.md#passfail) describes; for pass-with-drift, test again at the pinned commit. If 90% is stricter than you need, run the individual workflows or `forge` instead, which use the default threshold.
|
|
@@ -113,7 +119,7 @@ Rename rewrites the skill's `name` fields, the name where the snippet template w
|
|
|
113
119
|
|
|
114
120
|
Surfaced by `@Ferris RS` (exit `5`) when another rename of the same skill holds its run lock, `.skf-rename-<name>.lock` in `forge_data_folder`, and the lock has not gone stale (60 minutes after it was taken or renewed). A rename that waited at a question past that time stops the same way before it copies anything, because another rename may have run in the meantime. `--dry-run` takes no lock.
|
|
115
121
|
|
|
116
|
-
**Fix:** wait until the other rename ends, then run the rename again. If no rename of that skill is running, delete the lock file the message names, or wait until the time it gives.
|
|
122
|
+
**Fix:** wait until the other rename ends, then run the rename again. If no rename of that skill is running (a closed session or a cut-off `claude -p` run leaves its lock behind), delete the lock file the message names, or wait until the time it gives.
|
|
117
123
|
|
|
118
124
|
### Rename Skill stops with `name-collision` and lists old folders
|
|
119
125
|
|
|
@@ -147,7 +153,7 @@ Surfaced by `@Ferris US` (under `--headless`, `error.phase` is `merge:new-versio
|
|
|
147
153
|
|
|
148
154
|
Surfaced by `@Ferris US` when another update of the same skill holds its run lock, `.skf-update.lock` in `<forge_data_folder>/<name>/` (under `--headless`, `error.phase` is `init:concurrency-guard`). The message names the update that holds the lock, the lock file, and when the lock goes stale (60 minutes after it was taken or renewed). `--detect-only` and `--dry-run` take no lock.
|
|
149
155
|
|
|
150
|
-
**Fix:** wait until the other update ends, then re-run. If no update of that skill is running (a session that crashed or was closed leaves its lock behind), delete the lock file the message names, or wait until the time it gives. An update that finds a stale lock takes it over and undoes what the interrupted update left half written.
|
|
156
|
+
**Fix:** wait until the other update ends, then re-run. If no update of that skill is running (a session that crashed or was closed, or a `claude -p` run cut off when its agent ended its turn, leaves its lock behind), delete the lock file the message names, or wait until the time it gives. An update that finds a stale lock takes it over and undoes what the interrupted update left half written.
|
|
151
157
|
|
|
152
158
|
**`run-lock-lost`:** the same status with this reason means the update waited at a question past the stale time, so another update may have changed the skill (`error.phase` is `merge:run-lock` or `write:run-lock`). It stops before it writes `metadata.json`, and the halt undoes anything the update already wrote: it removes the new version folders, or, for a repair from a test report, restores the files it edited in place from the copy it took first, and the message says which. Re-run the update once no other update of that skill runs; if a version folder the message names is still on disk, delete it first.
|
|
153
159
|
|
|
@@ -44,13 +44,13 @@ Provenance maps live in `forge-data/{skill}/{version}/provenance-map.json` in th
|
|
|
44
44
|
|
|
45
45
|
This is the published entry for cognee's `search` in oh-my-skills' [`forge-data/oms-cognee/1.6.2/provenance-map.json`](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.6.2/provenance-map.json), shortened to its first two parameters (`search` takes 27). It points at line 41, where `async def search(` is defined at cognee v1.6.2's commit (`ba3631f2`), and the skill's `SKILL.md` cites `[AST:cognee/api/v1/search/search.py:L41]`. Its labels follow the tool that read it: an ast-grep match gives `T1`, `ast-grep`, the node kind its rule matched and an `[AST:]` citation. An export read by eye instead, because ast-grep could not parse its file, an ast-grep call that failed or timed out left its file unread, or no rule matched it, gets `T1-low`, `source-read`, no node kind and an `[SRC:]` citation, at every forge tier.
|
|
46
46
|
|
|
47
|
-
The 1.6.2 map passes the [line, label and citation checks](#workflow-time-enforcement) that SKF 3.0.0 added. SKF 4.0.0's Test Skill first blocked the skill and, after one Update Skill repair, passed it at 99.00%. Strict re-tests then blocked it again. After fixes by hand, one strict test was still blocked on three borderline calls, and a later one on the same documentation, which judged those calls as matches, passed it at 99% (see [the scores](#the-scores-including-the-ones-we-lose)). The older map next to it, [`forge-data/oms-cognee/1.0.0/provenance-map.json`](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.0.0/provenance-map.json), was compiled before labels followed the tool and still shows what those checks catch. It records `search` as `source-read` but labels it `T1`, and it points at line 26, the blank line above the definition, while that version's `SKILL.md` cites `[AST:cognee/api/v1/search/search.py:L27]`. A source read is `T1-low` with an `[SRC:]` citation, and its line is 27, the `def` line at v1.0.0.
|
|
47
|
+
The 1.6.2 map passes the [line, label and citation checks](#workflow-time-enforcement) that SKF 3.0.0 added. SKF 4.0.0's Test Skill first blocked the skill and, after one Update Skill repair, passed it at 99.00%. Strict re-tests then blocked it again. After fixes by hand, one strict test was still blocked on three borderline calls, and a later one on the same documentation, which judged those calls as matches, passed it at 99% (see [the scores](#the-scores-including-the-ones-we-lose)). Test Skill's signature rules now settle such calls instead of leaving them to the judge. The older map next to it, [`forge-data/oms-cognee/1.0.0/provenance-map.json`](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.0.0/provenance-map.json), was compiled before labels followed the tool and still shows what those checks catch. It records `search` as `source-read` but labels it `T1`, and it points at line 26, the blank line above the definition, while that version's `SKILL.md` cites `[AST:cognee/api/v1/search/search.py:L27]`. A source read is `T1-low` with an `[SRC:]` citation, and its line is 27, the `def` line at v1.0.0.
|
|
48
48
|
|
|
49
49
|
### 3. Visit the upstream repo at the pinned commit
|
|
50
50
|
|
|
51
51
|
Open `{source_repo}` at `{source_commit}`, jump to `{source_file}` line `{source_line}`. The signature in `SKILL.md` should match what you see in the source.
|
|
52
52
|
|
|
53
|
-
If it doesn't, **that's a bug**. For a skill you built, Test Skill (`@Ferris TS`) rates a signature mismatch as a Critical gap, which stops the test at its [hard gate](#hard-gate). Two differences a caller never meets are rated lower and still count as matching: a renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass arguments by position, is a Low gap, and an optional parameter left out of a signature the skill labels partial (such as `Props (simplified)`) is an Info observation. A renamed Python parameter stays Critical, because Test Skill cannot see which parameters a caller must pass by position, and so does a renamed Vue component prop, which callers set by name. Update Skill (`@Ferris US`) with `--from-test-report` repairs the gaps a test report lists. If you think SKF extracted it wrong, [open an issue](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/new/choose). Falsifiability isn't a feature. It's the whole deal.
|
|
53
|
+
If it doesn't, **that's a bug**. For a skill you built, Test Skill (`@Ferris TS`) rates a signature mismatch as a Critical gap, which stops the test at its [hard gate](#hard-gate). It compares only what a signature writes: a component by its props alone, so `FC<P>` and `(props: P) => JSX.Element` are one signature, and a type or a return type the skill does not write, or type arguments it leaves out (`dict` against `dict[str, Any]`), are not compared. When the skill documents a name in several places, such as a brief usage in `SKILL.md` and a full props list in a reference file, each form that lists parameters or gives a usage is compared, and a `const` holding a component, such as a `forwardRef(...)` result, is compared by the usage or declaration the skill writes for it. Two differences a caller never meets are rated lower and still count as matching: a renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass arguments by position, is a Low gap, and an optional parameter left out of a signature the skill labels partial (such as `Props (simplified)`) is an Info observation. A renamed Python parameter stays Critical, because Test Skill cannot see which parameters a caller must pass by position, and so does a renamed Vue component prop, which callers set by name. Update Skill (`@Ferris US`) with `--from-test-report` repairs the gaps a test report lists. If you think SKF extracted it wrong, [open an issue](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/new/choose). Falsifiability isn't a feature. It's the whole deal.
|
|
54
54
|
|
|
55
55
|
### Workflow-time enforcement
|
|
56
56
|
|
|
@@ -87,7 +87,7 @@ The [scoring formula](#how-the-score-is-computed) is deterministic and the pass
|
|
|
87
87
|
|
|
88
88
|
Take oh-my-skills' four reference skills as an example. Each first failed the [hard gate](#hard-gate) on SKF 4.0.0's stricter checks and was repaired by Update Skill with `--from-test-report`. Three then passed at 99.00% to 100%, and oms-storybook-react-vite at 95.17% (after an earlier 99.93% that was reverted).
|
|
89
89
|
|
|
90
|
-
The oms-cocoindex, oms-cognee and oms-storybook-react-vite passes read the signature rules leniently. A short `SKILL.md` signature that left out parameters its reference lists counted as a match, and so did a prop list missing optional props or `dict` against `dict[str, Any]`. Strict re-tests
|
|
90
|
+
The oms-cocoindex, oms-cognee and oms-storybook-react-vite passes read the signature rules leniently. A short `SKILL.md` signature that left out parameters its reference lists counted as a match, and so did a prop list missing optional props or `dict` against `dict[str, Any]`. Strict re-tests applied the rule as it was then written, wrote down how they read the cases it did not settle ([#843](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/843), [#845](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/845)), and said which calls they counted. They blocked all four at first. Test Skill's signature rules now settle how a component's props compare, and that a type, a return type or type arguments a signature leaves out (`dict` against `dict[str, Any]`) are not compared. oms-uitripled's strict re-test was blocked only because it compared `cn` with the declaration the plan named, not the one the skill cites; its other 265 signatures matched.
|
|
91
91
|
|
|
92
92
|
Most of the real errors were fixed against the pinned source. Update Skill repaired 22 storybook signatures. The rest were fixed by hand, each in a labeled commit:
|
|
93
93
|
- Two storybook fixes were needed because Update Skill halted on a gap that cited Angular's declaration; repairing from that citation would have copied Angular's signature into a React skill ([#824](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/824), [#830](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/830)).
|
|
@@ -96,14 +96,14 @@ Most of the real errors were fixed against the pinned source. Update Skill repai
|
|
|
96
96
|
|
|
97
97
|
Two storybook errors that block the gate, and the errors the reports record without blocking, are still open ([oh-my-skills#17](https://github.com/armelhbobdad/oh-my-skills/issues/17)).
|
|
98
98
|
|
|
99
|
-
Today, on a strict test of the version each one ships, oms-cocoindex passes at **100%** and oms-cognee at **99%**; both passes turn on borderline calls their reports write down. oms-uitripled is blocked by one gap oh-my-skills judges to be a false positive. oms-storybook-react-vite is blocked by two false positives and two older real errors that are not fixed yet.
|
|
99
|
+
Today, on a strict test of the version each one ships, oms-cocoindex passes at **100%** and oms-cognee at **99%**; both passes turn on borderline calls their reports write down, which Test Skill's signature rules now settle. oms-uitripled is blocked by one gap oh-my-skills judges to be a false positive. oms-storybook-react-vite is blocked by two false positives and two older real errors that are not fixed yet.
|
|
100
100
|
|
|
101
101
|
Every report, the failing ones included, is kept: in the skill's `forge-data/` folder, or, for that reverted repair, in oh-my-skills' git history. Even a passing report names the drift it found:
|
|
102
102
|
|
|
103
103
|
| Skill | Score | What the report discloses |
|
|
104
104
|
|---|---|---|
|
|
105
|
-
| [oms-cocoindex](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cocoindex/1.0.0/test-report-oms-cocoindex-20261103T131524Z-5907fba9.md) | **100%** | 79/79 exports on the brief's `scope.include` union (95 files), 27/27 signatures, 36/36 types. Nine borderline calls are recorded and not counted
|
|
106
|
-
| [oms-cognee](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.6.2/test-report-oms-cognee-20261103T134151Z-12e5ccdf.md) | **99%** | 92/92 exports of the `cognee/__init__.py` barrel at pinned commit `ba3631f2` (v1.6.2), 72/72 signatures, 35/35 types. External Validation is 90%: skill-check finds the `SKILL.md` body over its 5,000-token budget. The pass turns on borderline calls the report writes down: an earlier strict run counted three of them as Critical gaps ([#845](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/845), [#835](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/835)) and was blocked. |
|
|
105
|
+
| [oms-cocoindex](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cocoindex/1.0.0/test-report-oms-cocoindex-20261103T131524Z-5907fba9.md) | **100%** | 79/79 exports on the brief's `scope.include` union (95 files), 27/27 signatures, 36/36 types. Nine borderline calls are recorded and not counted: bare `*args` and `**kwargs` against `P.args` and `P.kwargs`, unannotated parameters and unstated returns. Test Skill's signature rules now read each of those as a match; one other call, `mount_each` documented with a `process_fn` parameter the source names `fn`, is a renamed Python parameter, so under those rules it is a Critical gap until the reference is corrected. The first strict re-test of the text behind its earlier 100% found seven `SKILL.md` signatures that misnamed parameters or left out `**kwargs` or an optional parameter. Left open: method signatures in the reference files that the report records as warnings without a gap ([oh-my-skills#17](https://github.com/armelhbobdad/oh-my-skills/issues/17)). |
|
|
106
|
+
| [oms-cognee](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.6.2/test-report-oms-cognee-20261103T134151Z-12e5ccdf.md) | **99%** | 92/92 exports of the `cognee/__init__.py` barrel at pinned commit `ba3631f2` (v1.6.2), 72/72 signatures, 35/35 types. External Validation is 90%: skill-check finds the `SKILL.md` body over its 5,000-token budget. The pass turns on borderline calls the report writes down: an earlier strict run counted three of them as Critical gaps ([#845](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/845), [#835](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/835)) and was blocked. Test Skill's signature rules now settle them: an overload set is read through its implementation, a bare `*args`, an unannotated parameter, an unstated return and omitted type arguments match, and a documented return type that matches what the function's body returns, against a declared annotation that says otherwise, is an Info observation that blocks nothing, recorded only when Test Skill's scoring script checks the return line the judge names. |
|
|
107
107
|
| [oms-storybook-react-vite](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-storybook-react-vite/10.3.5/test-report-oms-storybook-react-vite-20261103T131516Z-6e2b972a.md) | **blocked** | 4 Critical signature gaps, so no score. Two compare React wrappers with core's functions, false positives ([#781](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/781)). Two are real errors written in April and not fixed yet: the react-vite preset's `viteFinal` typed `PresetProperty<'viteFinal'>`, which resolves to `never` because `StorybookConfigRaw` has no `viteFinal` key, and `useParameter`'s parameters ([oh-my-skills#17](https://github.com/armelhbobdad/oh-my-skills/issues/17)). Signature Accuracy is 82/86 and Type Coverage 98/120. Export Coverage reads 100% only because the stored count then credited every documented name ([#792](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/792)). In the last run that counted them, 204 of the 280 names on the brief's `scope.tier_a_include` were documented, and that list misses names another package also declares ([#830](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/830)). Test Skill now counts only the names on that list, so the next run reads lower and lists the ones the skill does not document. |
|
|
108
108
|
| [oms-uitripled](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-uitripled/0.1.0/test-report-oms-uitripled-20261103T100001Z-981f84e6.md) | **blocked** | One Critical false positive: two exports share the name `cn`, and the test compared the one the skill does not document ([#781](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/781)). Under the strict rule, 265 of 266 signatures and every destructured component props list match. One props type, `NativeHoverCardProps`, writes `size` as required where the source has `size?` (an Info gap, still open). Export Coverage 100% against the stored count of 327, Type Coverage 42/42. |
|
|
109
109
|
|
|
@@ -115,7 +115,7 @@ A first round of SKF 4.0.0 repairs of oms-storybook-react-vite, three Update Ski
|
|
|
115
115
|
|
|
116
116
|
After the tiered brief, two re-tests were blocked and repaired by Update Skill before a third scored the skill at 95.17%. That number was still too high, for two reasons:
|
|
117
117
|
- Export Coverage read 100% because the stored count then credited every documented name ([#792](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/792)).
|
|
118
|
-
- Its signature judges read the doc-block prop lists leniently, a call no rule
|
|
118
|
+
- Its signature judges read the doc-block prop lists leniently, a call no rule settled then. Test Skill's signature rules now say how a props list compares.
|
|
119
119
|
|
|
120
120
|
Removing the stored count made the next run name every undocumented export. It measured 206 of the 280 authored names documented, and it found real errors, such as `definePreview` listed under `storybook/preview-api`. Update Skill repaired 22 signatures from a later report. Two fixes were made by hand because its repair halted: the test had cited Angular's `definePreview`, and Update Skill would have copied that signature into a React skill ([#824](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/824), [#830](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/830)). The other hand fixes, also checked against the pinned source, corrected errors no test gap covered and a later re-test's `DocsContainer` and Canvas gaps.
|
|
121
121
|
|
|
@@ -136,7 +136,7 @@ The score is a weighted sum of up to five categories. The weights below are the
|
|
|
136
136
|
| Category | Weight | What it measures |
|
|
137
137
|
|---|---|---|
|
|
138
138
|
| **Export Coverage** | 36% | Percentage of source exports documented in `SKILL.md` |
|
|
139
|
-
| **Signature Accuracy** | 22% | Documented function signatures match actual source signatures (parameter names, types, order, return types). A renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass every argument by position, still matches, and so does a signature the skill labels partial that leaves out only optional parameters |
|
|
139
|
+
| **Signature Accuracy** | 22% | Documented function signatures match actual source signatures (parameter names, types, order, return types). A renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass every argument by position, still matches, and so does a signature the skill labels partial that leaves out only optional parameters. A component is compared by its props alone, a type or return type a signature does not write is not compared, and each form a name is documented in is compared on its own |
|
|
140
140
|
| **Type Coverage** | 14% | Types and interfaces referenced in exports are fully documented |
|
|
141
141
|
| **Coherence** | 18% | Cross-references resolve, integration patterns are complete (contextual mode only) |
|
|
142
142
|
| **External Validation** | 10% | skill-check quality score (0–100), averaged with the Tessl Review score (0–100) when you opt in to Tessl Review |
|
|
@@ -225,7 +225,7 @@ Each step rates every gap it records against the Gap Severity table in Test Skil
|
|
|
225
225
|
| Severity | Examples |
|
|
226
226
|
|---|---|
|
|
227
227
|
| **Critical** | A wrong signature (any difference but the renamed parameter and the partial signature of the Low and Info rows), or a fabricated one (a documented export that the source does not define at the line the skill cites); a broken reference in a stack skill (a file, skill, type, or `scripts/` or `assets/` file that does not exist) |
|
|
228
|
-
| **High** | An inaccurate reference in a stack skill (the target exists but does not match), or a reference whose real path leads outside the skill and its sources; a split-body mismatch (`SKILL.md`
|
|
228
|
+
| **High** | An inaccurate reference in a stack skill (the target exists but does not match), or a reference whose real path leads outside the skill and its sources; a split-body mismatch (two files of the skill, `SKILL.md` or a `references/` file, document one export differently); `metadata.json` stats that count every export as documented while some are missing from the skill; in an individual skill, a missing required section, an unbalanced code fence, or an example that awaits a sync function or method or skips the `await` of an async one (a method call the check cannot tie to its class is listed but not checked) |
|
|
229
229
|
| **Medium** | A missing export or type (a missing export lowers Export Coverage); stale documentation (a documented export outside the coverage surface that the run did not match to one declaration in the source, or that a brief scopes out); an incomplete integration pattern in a stack skill; in an individual skill, a code fence with no language tag, a function no example names, or a table row whose column count differs from its header |
|
|
230
230
|
| **Low** | Gaps in metadata, the description or provenance lines, such as missing optional metadata or a provenance line that is not the definition line; a renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass arguments by position |
|
|
231
231
|
| **Info** | Observations such as style suggestions or a documented extra (a documented name outside the coverage surface that the source still declares); a partial signature (an optional parameter left out of a signature the skill labels partial); discovery testing not performed |
|
package/docs/workflows.md
CHANGED
|
@@ -95,7 +95,7 @@ Each workflow is also a skill you can run directly, without Ferris: `/skf-setup`
|
|
|
95
95
|
|
|
96
96
|
**Versions:** An update that writes produces a new version of the skill, in a folder of its own beside the previous version, which stays unchanged. For a skill built from a remote repository, the new version is the source's version when that is higher; otherwise it is the next patch version. `active` then points at the new version, and the next update starts from it, exported or not. Update Skill stops rather than overwrite a version that already exists. A gap-driven run updates the current version in place.
|
|
97
97
|
|
|
98
|
-
**Modes:** By default, Update Skill compares the skill with its source and rebuilds what changed. To repair the gaps a failed test found, run `@Ferris US <name> --from-test-report`: it reads the newest test report and fixes the skill at its pinned commit. It routes each gap by its category, so a missing export, which Test Skill rates Medium, is still re-extracted from the files its remediation names. A default run first looks for an unconsumed test report: a failed or pass-with-drift report newer than the skill that no repair has applied. It then offers to repair that report's gaps (`[G]`) or to check the source for changes (`[S]`), and a headless run checks the source and adds an `unconsumed-test-report` warning. Stack skills are not updated here. Update Skill sends you to `@Ferris SS` to rebuild the stack.
|
|
98
|
+
**Modes:** By default, Update Skill compares the skill with its source and rebuilds what changed. To repair the gaps a failed test found, run `@Ferris US <name> --from-test-report`: it reads the newest test report and fixes the skill at its pinned commit. It routes each gap by its category, so a missing export, which Test Skill rates Medium, is still re-extracted from the files its remediation names, never from one the skill brief's scope leaves out. A default run first looks for an unconsumed test report: a failed or pass-with-drift report newer than the skill that no repair has applied. It then offers to repair that report's gaps (`[G]`) or to check the source for changes (`[S]`), and a headless run checks the source and adds an `unconsumed-test-report` warning. Stack skills are not updated here. Update Skill sends you to `@Ferris SS` to rebuild the stack.
|
|
99
99
|
|
|
100
100
|
**Preview:** `--detect-only` lists the changes and stops. `--dry-run` also re-extracts and shows what would change. Neither writes anything.
|
|
101
101
|
|
|
@@ -245,7 +245,7 @@ Default pass threshold: **80%**. Inside a pipeline the default follows the alias
|
|
|
245
245
|
|
|
246
246
|
**Skill name:** A headless run with no skill name halts `input-missing`; an interactive run lists the skills there are to test and asks which one.
|
|
247
247
|
|
|
248
|
-
**Verdicts and exit codes (headless):** Each outcome has its exit code: `0` PASS, `2` FAIL, `3` INCONCLUSIVE, `4` pass-with-drift, and `1` for a halt before any verdict. A skill run cannot set the exit status of the agent that runs it (`claude -p` exits `0` whatever the run decided), so automators read `exit_code` from the `SKF_TEST_RESULT_JSON` line, or
|
|
248
|
+
**Verdicts and exit codes (headless):** Each outcome has its exit code: `0` PASS, `2` FAIL, `3` INCONCLUSIVE, `4` pass-with-drift, and `1` for a halt before any verdict. A skill run cannot set the exit status of the agent that runs it (`claude -p` exits `0` whatever the run decided), so automators read `exit_code` from the run's own `SKF_TEST_RESULT_JSON` line, which in headless mode is the run's last line. A run that printed no such line has no result to read, whatever `claude -p` exits with: it was cut off, or could not write its result, and `skf-test-skill-result-latest.json` may then hold an earlier run's verdict, so never read it in the line's place. A run the hard gate blocks on a Critical or High gap is a FAIL as well: it still writes the Gap Report and a FAIL result record, runs `on_complete` and the health check, and its result line, printed on stderr instead of stdout, has `exit_code` `2`, `status: "error"`, `halt_reason: "hard-gate-blocked"` and no score.
|
|
249
249
|
|
|
250
250
|
**Agent:** Ferris (Audit mode)
|
|
251
251
|
|
|
@@ -476,7 +476,7 @@ An alias given without its argument asks for it, and a headless run, such as one
|
|
|
476
476
|
|
|
477
477
|
- Pipelines **automatically turn on headless mode**: every confirmation gate auto-proceeds with its default action
|
|
478
478
|
- **Data flows automatically**: once the first workflow completes, its brief path or skill name becomes the input for the next workflows
|
|
479
|
-
- **Circuit breakers** stop the pipeline when a step's result is not good enough: AN finds nothing worth making into a skill, CS hits a hard error, AS finds CRITICAL drift, TS settles any verdict but PASS, or VS covers none of the technologies the architecture keeps. TS stops the pipeline on every FAIL (a Critical or High gap, a score below its threshold, or a FAIL that a cap forced although the score clears the threshold), on INCONCLUSIVE and on pass-with-drift. A TS score between 80% and a higher pipeline target (90% for `forge-auto`) passes with a fallback evidence report. VS stops the pipeline only on zero coverage, so a `NOT_FEASIBLE` verdict with coverage goes on to RA, which takes each `Blocked` pair as a critical issue.
|
|
479
|
+
- **Circuit breakers** stop the pipeline when a step's result is not good enough: AN finds nothing worth making into a skill, CS hits a hard error, AS finds CRITICAL drift, TS settles any verdict but PASS, or VS covers none of the technologies the architecture keeps. TS stops the pipeline on every FAIL (a Critical or High gap, a score below its threshold, or a FAIL that a cap forced although the score clears the threshold), on INCONCLUSIVE and on pass-with-drift. A TS score between 80% and a higher pipeline target (90% for `forge-auto`) passes with a fallback evidence report. VS stops the pipeline only on zero coverage, so a `NOT_FEASIBLE` verdict with coverage goes on to RA, which takes each `Blocked` pair as a critical issue. A workflow that printed no result line stops the pipeline with halt reason `no-result` (SF: with the line it displayed, if any): it was cut off, or could not write its result, so there is nothing to check.
|
|
480
480
|
- **`maintain` skips what it does not need.** When AS finds no drift (CLEAN), Update Skill is skipped, unless the source moved past the ref the skill was built from: then US runs with `--target-ref <upstream_ref>`, the ref the audit found. When AS finds CRITICAL drift, `maintain` stops so you can review the drift report; then run `@Ferris US <name>` yourself.
|
|
481
481
|
- **Resume:** each pipeline keeps its state in a journal, `pipeline-journal.json`, in its own run folder under `_bmad-output/.skf-run/`, written after every workflow. If one stopped partway, the next time you start Ferris he offers to rerun the step it stopped on with the recorded plan, then the rest. A quality halt gets its repair route instead: after a TS FAIL, `US <name> --from-test-report`, then TS and EX at the recorded threshold. You can also drop the offer, which deletes that run folder. When the chain ends, Ferris writes its result to `pipeline-result-latest.json` in his sidecar folder.
|
|
482
482
|
- **Anti-pattern warnings**: Ferris warns if you chain workflows in a problematic order (for example, exporting before testing)
|
|
@@ -576,7 +576,7 @@ Setup is not the only workflow whose result line is the run's final message: a h
|
|
|
576
576
|
- **`/skf-rename-skill` (RS)**: exit `2` `input-missing`/`input-invalid`, `3` resolution-failure (`manifest-corrupt`, `nothing-to-rename`), `4` write-failure (`copy-failed`, `write-failed`, `manifest-write-failed`; the context-file rebuild is best-effort and never halts), `5` state-conflict (`name-collision`, `source-authority-blocked`, `halted-for-concurrent-run` while another rename of the same skill holds its run lock or when this run's own lock went stale while it waited at a prompt, `not-skf-output` for a folder SKF did not generate, or a forge folder that also holds other files or that is a link, is not a folder, or cannot be listed; `flat-layout` for a skill not yet migrated; `verify-failed` when the old name is still in the renamed files, which rolls the rename back with the old skill intact, see [Troubleshooting](/docs/troubleshooting.md)). The run lock is `.skf-rename-<name>.lock` in `forge_data_folder`, and it goes stale 60 minutes after it was taken or renewed: when no other rename of that skill is running, delete the lock file the message names, or wait until the time it gives, then run the rename again. When this run's own lock went stale at a prompt, nothing was changed: run the rename again. Exit codes in [`src/skf-rename-skill/references/exit-codes.md`](https://github.com/armelhbobdad/bmad-module-skill-forge/blob/main/src/skf-rename-skill/references/exit-codes.md); the envelope in [`src/skf-rename-skill/references/invocation-contract.md`](https://github.com/armelhbobdad/bmad-module-skill-forge/blob/main/src/skf-rename-skill/references/invocation-contract.md) § "Result Contract (Headless)".
|
|
577
577
|
- **`/skf-refine-architecture` (RA)**: exit `2` `input-missing`/`input-invalid` (`input-invalid` also covers a Verify Stack report with a `schemaVersion` other than `1.0`, a missing or doubled verdict table, or an unknown verdict token), `3` resolution-failure (`output-folder-unconfigured`, `forge-folder-unconfigured`, and `resolution-failure` when the result emitter or `scripts/skf-check-preservation.py` cannot start, when the refinement rules cannot be read, lack one of their six tables or break a tier rule (phase `init:rules`), or when the skill inventory helper (phase `init:inventory`) or the co-mention helper (phase `gap-analysis:comention`) is missing or its call fails, or the Verify Stack report validator (phase `init:feasibility-validator`) is missing), `4` write-failure (`write-failed`, and `preservation-failed` when the draft would lose or change a line of the original: at once in a headless run, or when the promotion still finds it after one rebuild), `5` state-conflict (`insufficient-skills`: no skill SKF generated was found), `7` `inventory-unreliable`, `8` `recovery-failed` (at step 3, the Verify Stack report cannot be read again, Verify Stack rewrote it during the run, or the refinement rules no longer pass step 1's check; the saved state is not enough to rebuild the findings; or a record of the draft's build or promotion is missing at step 6). Exit codes and the envelope in [`src/skf-refine-architecture/references/exit-codes.md`](https://github.com/armelhbobdad/bmad-module-skill-forge/blob/main/src/skf-refine-architecture/references/exit-codes.md).
|
|
578
578
|
|
|
579
|
-
**Reading the result lines.** Check each line's `status` field first (for US, `skf_update.status`). For most workflows it only says whether the run finished or stopped on an error, so read the outcome from the fields below: a TS run whose skill scores below its threshold still reports `status: "success"`, with `verdict: "FAIL"`. AN, CS, SS, AS, TS, EX and VS also give each outcome a stable exit code, which the line carries as `exit_code` (a CS halt has `2` to `6`).
|
|
579
|
+
**Reading the result lines.** Read the run's own line: a run that printed none has no result to read, whatever the process exit code (`claude -p` exits `0` when the agent ends its turn early): it was cut off, or could not write its result. Treat it as a failure, and never read a `-latest.json` result file in its place, since that file may then describe an earlier run. A cut-off run can also leave its run lock until the lock goes stale, 60 minutes after it was taken or renewed: Test Skill's `.test-skill.lock`, Update Skill's `.skf-update.lock` or Rename Skill's `.skf-rename-<name>.lock` ([Troubleshooting](/docs/troubleshooting.md#test-skill-stops-with-another-run-active)). Check each line's `status` field first (for US, `skf_update.status`). For most workflows it only says whether the run finished or stopped on an error, so read the outcome from the fields below: a TS run whose skill scores below its threshold still reports `status: "success"`, with `verdict: "FAIL"`. AN, CS, SS, AS, TS, EX and VS also give each outcome a stable exit code, which the line carries as `exit_code` (a CS halt has `2` to `6`).
|
|
580
580
|
|
|
581
581
|
| Workflow | Result line | Fields worth reading |
|
|
582
582
|
| --- | --- | --- |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "bmad-module-skill-forge",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.0",
|
|
5
5
|
"description": "BMAD module — Turn code and docs into instructions AI agents can actually follow. Progressive capability tiers (Quick/Forge/Forge+/Deep).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bmad",
|
|
@@ -23,12 +23,12 @@ Bridge names (`ast_bridge`, `ccc_bridge`, `qmd_bridge`, `gh_bridge`) and subproc
|
|
|
23
23
|
|
|
24
24
|
Workflow steps reference "subprocess" execution with numbered patterns. These map to concrete mechanisms per IDE:
|
|
25
25
|
|
|
26
|
-
| Pattern | Purpose | Claude Code
|
|
27
|
-
|
|
28
|
-
| **Pattern 1** (grep/search) | Search across files for imports, co-imports | Grep tool or Bash with `rg`
|
|
29
|
-
| **Pattern 2** (per-file deep analysis) | Deep analysis of individual files or units | Agent tool (sequential per unit)
|
|
30
|
-
| **Pattern 3** (data operations) | QMD queries, severity classification, data transforms | Main thread (Read + process)
|
|
31
|
-
| **Pattern 4** (parallel execution) | Parallel per-library extraction, parallel diffing | Agent
|
|
26
|
+
| Pattern | Purpose | Claude Code | Cursor | CLI |
|
|
27
|
+
|----------------------------------------|-------------------------------------------------------|--------------------------------------------------------------------------|-----------------------------------|-------------------------------------|
|
|
28
|
+
| **Pattern 1** (grep/search) | Search across files for imports, co-imports | Grep tool or Bash with `rg` | Built-in search | `grep` / `rg` |
|
|
29
|
+
| **Pattern 2** (per-file deep analysis) | Deep analysis of individual files or units | Agent tool (foreground, sequential per unit) | Sequential analysis | Per-unit script |
|
|
30
|
+
| **Pattern 3** (data operations) | QMD queries, severity classification, data transforms | Main thread (Read + process) | Main thread | Script |
|
|
31
|
+
| **Pattern 4** (parallel execution) | Parallel per-library extraction, parallel diffing | Several foreground Agent calls in one message, never `run_in_background` | Parallel requests (IDE-dependent) | `xargs -P` (it waits for every job) |
|
|
32
32
|
|
|
33
33
|
### max_parallel_generation
|
|
34
34
|
|
|
@@ -81,9 +81,12 @@ A gate appends its decision the moment it decides, and a step appends a warning
|
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
83
|
uv run {emitEnvelopeHelper} record --workflow <workflow> --run-dir "{run_dir}" --decision < "{run_dir}/decision.json"
|
|
84
|
-
uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "<warning>"
|
|
84
|
+
uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "<warning>" [--warning "<another>"]
|
|
85
|
+
uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warnings-file "{run_dir}/<helper output>.json"
|
|
85
86
|
```
|
|
86
87
|
|
|
88
|
+
`--warning` repeats, one per warning. `--warnings-file` records, in one call, every warning in a helper's saved JSON output: an array of strings, or an object whose `warnings` is one. A blank or non-string warning, or a file with no such list, records nothing and exits 1; an empty list records nothing and exits 0.
|
|
89
|
+
|
|
87
90
|
With `--workflow`, a decision the workflow's envelope schema rejects fails here, at the gate; without it, the emitter leaves the decision out when the run ends and adds a `headless_decision_invalid` warning. Because the sink is on disk, the trail survives context compaction, and a HARD HALT reports every decision taken before it.
|
|
88
91
|
|
|
89
92
|
When the On Activation customization resolver is missing or fails, the run applies only the skill's own `customize.toml`. Record `customization_resolver_unavailable: <reason>` as a warning, or pass the reason in the envelope payload as `customization_resolver_unavailable`, so a pipeline sees that the team and user overrides under `_bmad/custom/` were not applied. Campaign's run spans several sessions, so it logs `customization_resolver_unavailable: <reason>` as an `event` in its decision log instead, and its result envelope's `decision_log` names that file.
|
|
@@ -31,14 +31,14 @@ Each run writes **two files** to `{output_dir}`:
|
|
|
31
31
|
- Timestamp is UTC, resolution to seconds, for example `update-skill-result-20260413-145230.json`
|
|
32
32
|
- When a run of the same workflow already took that second's name, the emitter appends `-2`, `-3` and so on (`update-skill-result-20260413-145230-2.json`), so two runs that end in the same second never share a file
|
|
33
33
|
- Never overwritten by subsequent runs, so it keeps a durable audit trail across retries, aborts, and re-runs
|
|
34
|
-
2. **Stable latest pointer** (
|
|
34
|
+
2. **Stable latest pointer** (a stable path, not a run's result): `{skill-name}-result-latest.json`
|
|
35
35
|
- A **copy** (not a symlink) of the per-run record just written
|
|
36
|
-
-
|
|
36
|
+
- At a deterministic path, so a reader finds `summary.*` without enumerating timestamps: it holds the last run whose emitter wrote this copy
|
|
37
37
|
- Overwritten on every successful write
|
|
38
38
|
|
|
39
39
|
Write the per-run record first, then copy it to the `-latest.json` path. If the copy fails, the per-run record still exists — the run is not lost.
|
|
40
40
|
|
|
41
|
-
**Consumers (forger, CI, chained workflows):** read
|
|
41
|
+
**Consumers (forger, CI, chained workflows):** a caller that started a run reads that run's own `SKF_<NAME>_RESULT_JSON` envelope line. A run that printed no envelope line has no result to read, whatever the process exit code: it was cut off, or could not write its result, and `{skill-name}-result-latest.json` may then describe an earlier run, so it is never read in its place. A tool that looks up the last run of a skill rather than one it started (skf-find-test-report.py, forge-status.py) may read the copy. Do not enumerate timestamped files unless inspecting prior-run history.
|
|
42
42
|
|
|
43
43
|
## Writing the Files
|
|
44
44
|
|
|
@@ -36,6 +36,7 @@ The forger also accepts common pipeline aliases:
|
|
|
36
36
|
4. **Circuit breakers** — if a workflow's output fails its quality check, the pipeline halts with a summary of what completed and what remains
|
|
37
37
|
5. **Error halts propagate** — if any workflow hard-halts, the pipeline stops immediately
|
|
38
38
|
6. **Progress reporting** — the forger reports completion of each workflow before starting the next
|
|
39
|
+
7. **A missing result line halts**: a workflow that printed no envelope line has no result to read, whatever the process exit code: it was cut off, or could not write its result, so the pipeline halts with `no-result` (SF: with the line it did display, or `no-result` when it displayed none), and the forger never reads a `-latest.json` file in its place
|
|
39
40
|
|
|
40
41
|
## Pipeline Arguments
|
|
41
42
|
|
|
@@ -117,7 +118,7 @@ Every chained workflow's data context carries `pipeline_mode: true`, which marks
|
|
|
117
118
|
|
|
118
119
|
## Pipeline Result
|
|
119
120
|
|
|
120
|
-
`pipeline-journal.py finish` writes each chain's result from its journal, per `output-contract-schema.md`: the per-run record `{sidecar_path}/pipeline-result-<YYYYMMDD-HHmmss>.json` (UTC; `-2`, `-3` ... when a run already took that second's name) and its copy `{sidecar_path}/pipeline-result-latest.json`, the file
|
|
121
|
+
`pipeline-journal.py finish` writes each chain's result from its journal, per `output-contract-schema.md`: the per-run record `{sidecar_path}/pipeline-result-<YYYYMMDD-HHmmss>.json` (UTC; `-2`, `-3` ... when a run already took that second's name) and its copy `{sidecar_path}/pipeline-result-latest.json`, which holds the last chain, or attempt, that reached `finish`. A chain cut off before `finish` writes none, so the file may describe an earlier chain or an earlier attempt of this one: a caller reads the envelope lines the chain's workflows print, never this file in their place.
|
|
121
122
|
|
|
122
123
|
```json
|
|
123
124
|
{
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"health-check-missing",
|
|
121
121
|
"hard-gate-blocked"
|
|
122
122
|
],
|
|
123
|
-
"description": "Null for a scored run. On a halt, the failure class: input-missing (headless with no skill name), input-invalid (a --tier value that names no tier), runtime-missing (python3 or uv is not on PATH), helper-missing (an SKF helper is not installed), helper-failed (a helper or scoring script exited with an error), write-failed (the run folder, the report or the result contract could not be written), target-inaccessible (no version of the skill to test), not-skf-output, frontmatter-invalid, forge-tier-missing, workspace-drift, another-run-active, inventory-invalid (the documented inventory failed its schema check
|
|
123
|
+
"description": "Null for a scored run. On a halt, the failure class: input-missing (headless with no skill name), input-invalid (a --tier value that names no tier), runtime-missing (python3 or uv is not on PATH), helper-missing (an SKF helper is not installed), helper-failed (a helper or scoring script exited with an error), write-failed (the run folder, the report or the result contract could not be written), target-inaccessible (no version of the skill to test), not-skf-output, frontmatter-invalid, forge-tier-missing, workspace-drift, another-run-active, inventory-invalid (the documented inventory failed its schema check, spot-check or floor check twice), indeterminate-surface (zero exports, or an empty stack surface: no score can be attached), step-completeness-violation, report-anchor-missing, health-check-missing; hard-gate-blocked for a run the hard gate blocked (verdict FAIL, exit 2)."
|
|
124
124
|
},
|
|
125
125
|
"run_id": {
|
|
126
126
|
"type": ["string", "null"],
|