bmad-module-skill-forge 1.9.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.nvmrc +1 -1
- package/README.md +10 -5
- package/docs/_data/pinned.yaml +2 -2
- package/docs/agents.md +11 -2
- package/docs/architecture.md +5 -4
- package/docs/bmad-synergy.md +30 -7
- package/docs/campaign.md +172 -0
- package/docs/examples.md +7 -3
- package/docs/forge-auto.md +90 -0
- package/docs/getting-started.md +9 -0
- package/docs/how-it-works.md +6 -4
- package/docs/index.md +4 -3
- package/docs/skill-model.md +1 -1
- package/docs/troubleshooting.md +17 -1
- package/docs/verifying-a-skill.md +2 -2
- package/docs/why-skf.md +1 -1
- package/docs/workflows.md +64 -24
- package/package.json +2 -2
- package/src/knowledge/skill-lifecycle.md +1 -1
- package/src/module-help.csv +4 -3
- package/src/shared/data/language-corpora.json +32 -0
- package/src/shared/references/pipeline-contracts.md +6 -3
- package/src/shared/scripts/schemas/skill-brief.v1.json +6 -1
- package/src/shared/scripts/skf-comention-pairs.py +350 -0
- package/src/shared/scripts/skf-count-tokens.py +222 -0
- package/src/shared/scripts/skf-derive-assembly-shape.py +107 -0
- package/src/shared/scripts/skf-detect-docs.py +240 -4
- package/src/shared/scripts/skf-detect-language.py +82 -3
- package/src/shared/scripts/skf-detect-tools.py +105 -2
- package/src/shared/scripts/skf-emit-result-envelope.py +17 -0
- package/src/shared/scripts/skf-enumerate-stack-skills.py +111 -2
- package/src/shared/scripts/skf-extract-public-api.py +53 -0
- package/src/shared/scripts/skf-find-cycles.py +246 -0
- package/src/shared/scripts/skf-hash-content.py +487 -2
- package/src/shared/scripts/skf-language-corpora.py +100 -0
- package/src/shared/scripts/skf-manifest-ops.py +67 -1
- package/src/shared/scripts/skf-merge-doc-urls.py +244 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-rebuild-managed-sections.py +180 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +1 -2
- package/src/shared/scripts/skf-render-metadata-stats.py +499 -0
- package/src/shared/scripts/skf-rewrite-skill-name.py +308 -0
- package/src/shared/scripts/skf-shape-detect.py +576 -25
- package/src/shared/scripts/skf-shard-body.py +461 -0
- package/src/shared/scripts/skf-skill-inventory.py +139 -3
- package/src/shared/scripts/skf-structural-diff.py +307 -67
- package/src/shared/scripts/skf-validate-brief-schema.py +41 -6
- package/src/shared/scripts/skf-validate-feasibility-report.py +234 -0
- package/src/shared/scripts/skf-validate-frontmatter.py +17 -12
- package/src/shared/scripts/skf-validate-output.py +443 -10
- package/src/shared/scripts/skf-validate-pins.py +3 -3
- package/src/shared/scripts/skf-verify-no-trace.py +214 -0
- package/src/shared/scripts/skf-verify-provenance-completeness.py +439 -0
- package/src/shared/scripts/skf-write-skill-brief.py +21 -5
- package/src/skf-analyze-source/SKILL.md +17 -32
- package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
- package/src/skf-analyze-source/customize.toml +2 -0
- package/src/skf-analyze-source/references/auto-docs-only.md +116 -0
- package/src/skf-analyze-source/references/continue.md +8 -13
- package/src/skf-analyze-source/references/generate-briefs.md +28 -12
- package/src/skf-analyze-source/references/headless-contract.md +33 -0
- package/src/skf-analyze-source/references/health-check.md +1 -2
- package/src/skf-analyze-source/references/identify-units.md +25 -36
- package/src/skf-analyze-source/references/init.md +12 -25
- package/src/skf-analyze-source/references/map-and-detect.md +8 -20
- package/src/skf-analyze-source/references/recommend.md +4 -14
- package/src/skf-analyze-source/references/scan-project.md +3 -11
- package/src/skf-analyze-source/references/step-auto-scope.md +211 -197
- package/src/skf-analyze-source/references/step-shape-detect.md +20 -13
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +2 -2
- package/src/skf-audit-skill/SKILL.md +9 -6
- package/src/skf-audit-skill/customize.toml +2 -0
- package/src/skf-audit-skill/references/init.md +58 -32
- package/src/skf-audit-skill/references/re-index.md +15 -34
- package/src/skf-audit-skill/references/report.md +11 -58
- package/src/skf-audit-skill/references/semantic-diff.md +4 -22
- package/src/skf-audit-skill/references/severity-classify.md +53 -87
- package/src/skf-audit-skill/references/step-doc-drift.md +49 -19
- package/src/skf-audit-skill/references/structural-diff.md +43 -68
- package/src/skf-brief-skill/SKILL.md +7 -36
- package/src/skf-brief-skill/assets/scope-templates.md +1 -1
- package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -1
- package/src/skf-brief-skill/customize.toml +14 -0
- package/src/skf-brief-skill/references/analyze-target.md +13 -18
- package/src/skf-brief-skill/references/confirm-brief.md +2 -15
- package/src/skf-brief-skill/references/draft-checkpoint.md +8 -8
- package/src/skf-brief-skill/references/gather-intent.md +27 -24
- package/src/skf-brief-skill/references/headless-args.md +1 -1
- package/src/skf-brief-skill/references/health-check.md +1 -22
- package/src/skf-brief-skill/references/invocation-contract.md +37 -0
- package/src/skf-brief-skill/references/scope-definition.md +6 -15
- package/src/skf-brief-skill/references/step-auto-brief.md +22 -9
- package/src/skf-brief-skill/references/step-auto-validate.md +3 -7
- package/src/skf-brief-skill/references/version-resolution.md +1 -1
- package/src/skf-brief-skill/references/write-brief.md +19 -13
- package/src/skf-campaign/SKILL.md +121 -0
- package/src/skf-campaign/assets/campaign-state-schema.json +191 -0
- package/src/skf-campaign/customize.toml +75 -0
- package/src/skf-campaign/manifest.yaml +15 -0
- package/src/skf-campaign/references/campaign-contracts.md +67 -0
- package/src/skf-campaign/references/campaign-directive-spec.md +32 -0
- package/src/skf-campaign/references/health-check.md +35 -0
- package/src/skf-campaign/references/step-01-setup.md +121 -0
- package/src/skf-campaign/references/step-02-strategy.md +102 -0
- package/src/skf-campaign/references/step-03-pins.md +56 -0
- package/src/skf-campaign/references/step-04-provenance.md +63 -0
- package/src/skf-campaign/references/step-05-skill-loop.md +103 -0
- package/src/skf-campaign/references/step-06-batch.md +88 -0
- package/src/skf-campaign/references/step-07-capstone.md +63 -0
- package/src/skf-campaign/references/step-08-verify.md +75 -0
- package/src/skf-campaign/references/step-09-refine.md +83 -0
- package/src/skf-campaign/references/step-10-export.md +106 -0
- package/src/skf-campaign/references/step-11-maintenance.md +84 -0
- package/src/skf-campaign/references/step-resume.md +115 -0
- package/src/skf-campaign/scripts/.gitkeep +0 -0
- package/src/skf-campaign/scripts/campaign-deps.py +244 -0
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +119 -0
- package/src/skf-campaign/scripts/campaign-provenance.py +247 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +199 -0
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +158 -0
- package/src/skf-campaign/scripts/campaign-report.py +249 -0
- package/src/skf-campaign/scripts/campaign-status.py +162 -0
- package/src/skf-campaign/scripts/campaign-validate-pins.py +174 -0
- package/src/skf-campaign/scripts/campaign-validate-state.py +207 -0
- package/src/skf-campaign/templates/campaign-brief-template.yaml +34 -0
- package/src/skf-campaign/templates/campaign-report-template.md +54 -0
- package/src/skf-campaign/templates/kickoff-template.md +48 -0
- package/src/skf-create-skill/SKILL.md +15 -4
- package/src/skf-create-skill/assets/compile-assembly-rules.md +35 -34
- package/src/skf-create-skill/assets/skill-sections.md +9 -100
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +4 -13
- package/src/skf-create-skill/customize.toml +13 -0
- package/src/skf-create-skill/references/authoritative-files-protocol.md +7 -7
- package/src/skf-create-skill/references/compile.md +37 -47
- package/src/skf-create-skill/references/component-extraction.md +8 -35
- package/src/skf-create-skill/references/ecosystem-check.md +9 -25
- package/src/skf-create-skill/references/enrich.md +4 -17
- package/src/skf-create-skill/references/extract.md +20 -27
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +1 -1
- package/src/skf-create-skill/references/extraction-patterns.md +10 -8
- package/src/skf-create-skill/references/generate-artifacts.md +20 -25
- package/src/skf-create-skill/references/health-check.md +2 -2
- package/src/skf-create-skill/references/load-brief.md +16 -18
- package/src/skf-create-skill/references/report.md +27 -14
- package/src/skf-create-skill/references/source-resolution-protocols.md +4 -4
- package/src/skf-create-skill/references/step-auto-shard.md +52 -25
- package/src/skf-create-skill/references/step-doc-rot.md +33 -15
- package/src/skf-create-skill/references/step-doc-sources.md +13 -4
- package/src/skf-create-skill/references/sub/ccc-discover.md +2 -13
- package/src/skf-create-skill/references/sub/fetch-docs.md +33 -46
- package/src/skf-create-skill/references/sub/fetch-temporal.md +5 -20
- package/src/skf-create-skill/references/tier-degradation-rules.md +6 -8
- package/src/skf-create-skill/references/validate.md +69 -29
- package/src/skf-create-skill/scripts/scan-doc-rot.py +245 -0
- package/src/skf-create-stack-skill/SKILL.md +15 -9
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +1 -1
- package/src/skf-create-stack-skill/customize.toml +10 -0
- package/src/skf-create-stack-skill/references/compile-stack.md +7 -8
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -13
- package/src/skf-create-stack-skill/references/detect-integrations.md +49 -54
- package/src/skf-create-stack-skill/references/detect-manifests.md +37 -30
- package/src/skf-create-stack-skill/references/generate-output.md +24 -75
- package/src/skf-create-stack-skill/references/init.md +9 -81
- package/src/skf-create-stack-skill/references/manifest-patterns.md +5 -35
- package/src/skf-create-stack-skill/references/parallel-extract.md +12 -27
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +6 -6
- package/src/skf-create-stack-skill/references/report.md +27 -62
- package/src/skf-create-stack-skill/references/validate.md +35 -31
- package/src/skf-drop-skill/SKILL.md +20 -23
- package/src/skf-drop-skill/customize.toml +10 -0
- package/src/skf-drop-skill/references/execute.md +67 -36
- package/src/skf-drop-skill/references/headless-contract.md +18 -0
- package/src/skf-drop-skill/references/health-check.md +1 -13
- package/src/skf-drop-skill/references/report.md +44 -10
- package/src/skf-drop-skill/references/select.md +93 -48
- package/src/skf-drop-skill/scripts/dir-sizes.py +116 -0
- package/src/skf-export-skill/SKILL.md +17 -14
- package/src/skf-export-skill/assets/managed-section-format.md +2 -5
- package/src/skf-export-skill/customize.toml +10 -6
- package/src/skf-export-skill/references/generate-snippet.md +21 -35
- package/src/skf-export-skill/references/load-skill.md +44 -42
- package/src/skf-export-skill/references/multi-skill-mode.md +1 -1
- package/src/skf-export-skill/references/orphan-context-detection.md +11 -3
- package/src/skf-export-skill/references/package.md +27 -33
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +24 -5
- package/src/skf-export-skill/references/result-envelope.md +28 -0
- package/src/skf-export-skill/references/summary.md +11 -5
- package/src/skf-export-skill/references/token-report.md +42 -24
- package/src/skf-export-skill/references/update-context.md +32 -33
- package/src/skf-forger/SKILL.md +32 -39
- package/src/skf-forger/references/pipeline-mode.md +34 -0
- package/src/skf-forger/scripts/parse-pipeline.py +270 -0
- package/src/skf-quick-skill/SKILL.md +10 -60
- package/src/skf-quick-skill/customize.toml +9 -0
- package/src/skf-quick-skill/references/batch-mode.md +6 -4
- package/src/skf-quick-skill/references/compile.md +15 -52
- package/src/skf-quick-skill/references/ecosystem-check.md +5 -20
- package/src/skf-quick-skill/references/finalize.md +12 -6
- package/src/skf-quick-skill/references/halt-contract.md +54 -0
- package/src/skf-quick-skill/references/health-check.md +3 -4
- package/src/skf-quick-skill/references/quick-extract.md +7 -18
- package/src/skf-quick-skill/references/registry-resolution.md +1 -23
- package/src/skf-quick-skill/references/resolve-target.md +27 -34
- package/src/skf-quick-skill/references/write-and-validate.md +10 -25
- package/src/skf-refine-architecture/SKILL.md +16 -8
- package/src/skf-refine-architecture/customize.toml +9 -0
- package/src/skf-refine-architecture/references/compile.md +10 -8
- package/src/skf-refine-architecture/references/gap-analysis.md +21 -50
- package/src/skf-refine-architecture/references/health-check.md +1 -1
- package/src/skf-refine-architecture/references/improvements.md +9 -25
- package/src/skf-refine-architecture/references/init.md +9 -16
- package/src/skf-refine-architecture/references/issue-detection.md +10 -25
- package/src/skf-refine-architecture/references/refinement-rules.md +4 -16
- package/src/skf-refine-architecture/references/report.md +14 -11
- package/src/skf-rename-skill/SKILL.md +11 -21
- package/src/skf-rename-skill/customize.toml +10 -0
- package/src/skf-rename-skill/references/execute.md +112 -82
- package/src/skf-rename-skill/references/exit-codes.md +12 -0
- package/src/skf-rename-skill/references/report.md +16 -8
- package/src/skf-rename-skill/references/select.md +69 -42
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -0
- package/src/skf-setup/SKILL.md +21 -13
- package/src/skf-setup/customize.toml +10 -0
- package/src/skf-setup/references/auto-index.md +11 -10
- package/src/skf-setup/references/ccc-index.md +14 -15
- package/src/skf-setup/references/detect-and-tier.md +10 -8
- package/src/skf-setup/references/report.md +26 -32
- package/src/skf-setup/references/tier-rules.md +2 -32
- package/src/skf-setup/references/write-config.md +9 -10
- package/src/skf-test-skill/SKILL.md +10 -8
- package/src/skf-test-skill/customize.toml +2 -0
- package/src/skf-test-skill/references/coherence-check.md +19 -31
- package/src/skf-test-skill/references/coverage-check.md +111 -67
- package/src/skf-test-skill/references/detect-mode.md +10 -41
- package/src/skf-test-skill/references/external-validators.md +11 -19
- package/src/skf-test-skill/references/init.md +39 -21
- package/src/skf-test-skill/references/migration-section-rules.md +4 -4
- package/src/skf-test-skill/references/report.md +60 -40
- package/src/skf-test-skill/references/score.md +32 -64
- package/src/skf-test-skill/references/scoring-rules.md +6 -62
- package/src/skf-test-skill/references/source-access-protocol.md +15 -5
- package/src/skf-test-skill/references/step-hard-gate.md +1 -3
- package/src/skf-test-skill/scripts/aggregate-coherence.py +276 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +359 -0
- package/src/skf-test-skill/scripts/combine-external-scores.py +190 -0
- package/src/skf-test-skill/scripts/compute-score.py +135 -4
- package/src/skf-test-skill/scripts/reconcile-coverage.py +327 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +251 -0
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +192 -0
- package/src/skf-update-skill/SKILL.md +17 -5
- package/src/skf-update-skill/customize.toml +11 -6
- package/src/skf-update-skill/references/detect-changes.md +48 -36
- package/src/skf-update-skill/references/health-check.md +5 -11
- package/src/skf-update-skill/references/init.md +40 -32
- package/src/skf-update-skill/references/manual-section-rules.md +1 -2
- package/src/skf-update-skill/references/merge-conflict-rules.md +1 -52
- package/src/skf-update-skill/references/merge.md +13 -57
- package/src/skf-update-skill/references/re-extract.md +27 -25
- package/src/skf-update-skill/references/report.md +14 -7
- package/src/skf-update-skill/references/validate.md +32 -30
- package/src/skf-update-skill/references/write.md +86 -51
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +167 -0
- package/src/skf-verify-stack/SKILL.md +11 -21
- package/src/skf-verify-stack/customize.toml +12 -3
- package/src/skf-verify-stack/references/coverage-patterns.md +1 -29
- package/src/skf-verify-stack/references/coverage.md +21 -10
- package/src/skf-verify-stack/references/exit-codes.md +14 -0
- package/src/skf-verify-stack/references/health-check.md +1 -1
- package/src/skf-verify-stack/references/init.md +20 -25
- package/src/skf-verify-stack/references/integration-verification-rules.md +10 -19
- package/src/skf-verify-stack/references/integrations.md +44 -49
- package/src/skf-verify-stack/references/report.md +28 -34
- package/src/skf-verify-stack/references/requirements.md +3 -3
- package/src/skf-verify-stack/references/synthesize.md +36 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +196 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +256 -0
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +258 -0
- package/tools/cli/lib/ui.js +3 -2
- package/docs/deepwiki.md +0 -89
- package/src/skf-rename-skill/references/rebuild-context.md +0 -110
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'coherence-check.md'
|
|
3
3
|
outputFile: '{forge_version}/test-report-{skill_name}-{run_id}.md'
|
|
4
|
-
scoringRulesFile: '
|
|
4
|
+
scoringRulesFile: '{scoringRulesPath}'
|
|
5
5
|
sourceAccessProtocol: 'references/source-access-protocol.md'
|
|
6
|
+
reconcileScript: 'scripts/reconcile-coverage.py'
|
|
7
|
+
coherenceScript: 'scripts/check-metadata-coherence.py'
|
|
8
|
+
numeratorVerifyScript: 'scripts/verify-declared-numerator.py'
|
|
6
9
|
---
|
|
7
10
|
|
|
8
11
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -35,10 +38,10 @@ Load `{sourceAccessProtocol}` and follow both sections:
|
|
|
35
38
|
|
|
36
39
|
<!-- Subagent delegation: read SKILL.md + references/*.md, return compact JSON inventory -->
|
|
37
40
|
|
|
38
|
-
Delegate reading of the skill under test to a subagent. The subagent receives the path to SKILL.md (and the `references/` directory path if it exists) and
|
|
41
|
+
Delegate reading of the skill under test to a subagent. The subagent receives the path to SKILL.md (and the `references/` directory path if it exists) and must:
|
|
39
42
|
1. Read SKILL.md
|
|
40
43
|
2. If a `references/` directory exists alongside SKILL.md and SKILL.md's `## Full` headings are absent or stubs, also read all `references/*.md` files
|
|
41
|
-
3.
|
|
44
|
+
3. only return this compact JSON inventory — no prose, no extra commentary:
|
|
42
45
|
|
|
43
46
|
```json
|
|
44
47
|
{
|
|
@@ -65,22 +68,32 @@ Delegate reading of the skill under test to a subagent. The subagent receives th
|
|
|
65
68
|
|
|
66
69
|
**Parent uses this JSON summary as the documented inventory.** Do not load SKILL.md or references file contents into parent context.
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
**If subagent delegation is unavailable:** the parent performs the read itself in the main thread — read SKILL.md (and, per step 2 above, all `references/*.md` when a `references/` directory exists and SKILL.md's `## Full` headings are absent or stubs) and assemble the same compact JSON inventory. The §1a schema validation and ground-truth spot-check still run on the parent-built inventory — a quality gate does not skip its own hallucination guards just because the extraction ran in-thread. This mirrors the §2 fallback ("perform ast-grep analysis in main thread") so §1 degrades gracefully instead of stalling.
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
#### 1a. Parent-Side Schema Validation + Spot-Check
|
|
71
74
|
|
|
72
|
-
|
|
75
|
+
test-skill is a quality gate — it must not trust subagent output blindly. Before any downstream step consumes the inventory, the parent runs a schema validator and a grep spot-check, and HALTs on any failure.
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
2. Required keys present: `exports` (list), `cross_check_mismatches` (list — may be empty). Missing key or wrong type → HALT "coverage-check: subagent JSON schema invalid — missing/typo: {key}". Note: the parent already knows the skill name from workflow context (`{resolved_skill_package}` from step 1) — the subagent is not required to echo it back, and doing so introduces a contract-drift surface without improving verification.
|
|
76
|
-
3. Each `exports[]` entry must be a dict with at minimum `name` (non-empty string) and `kind` (one of `function|class|type|constant|hook|interface|method|struct|enum|trait|macro|adapter`). The enum spans the constructs SKF actually documents across languages and skill types: JS/TS (`function`/`class`/`type`/`constant`/`hook`/`interface`/`method`), Rust public-API items (`struct`/`enum`/`trait`/`macro` — alongside the shared `type`/`constant`/`function`), and stack-composition scaffolds (`adapter`). Reject entries violating this; if >0 rejections, HALT "coverage-check: subagent returned malformed export entries — {count} entries do not match schema".
|
|
77
|
-
4. `cross_check_mismatches[]` entries (when non-empty) must carry `export`, `skill_md_line`, `reference_file`, `reference_line`, `issue`. Missing fields → HALT.
|
|
77
|
+
**Schema validation (required keys + types) — delegated to `scripts/validate-inventory.py`.** Fence-stripping, JSON parsing, and the required-keys / per-entry-type / `kind`-enum / mismatch-field contract are structural validation with one correct verdict per input, so they run in the script, not in-prompt. Pipe the subagent's **raw** response (fence and all) to it exactly as §2c pipes the reconcile input:
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
```bash
|
|
80
|
+
echo '<subagent raw response>' | uv run scripts/validate-inventory.py --stdin
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The script strips a wrapping markdown fence (a leading line of three backticks with an optional language tag like `json`, and a trailing line of three backticks — subagents frequently return fenced JSON despite instructions), parses the inner content, and enforces exactly this contract, returning a `violations[]` entry for each breach:
|
|
84
|
+
|
|
85
|
+
- Parses as JSON after fence stripping (parse failure → `not valid JSON`).
|
|
86
|
+
- Required keys present with correct types: `exports` (list), `cross_check_mismatches` (list — may be empty). Note: the parent already knows the skill name from workflow context (`{resolved_skill_package}` from step 1) — the subagent is not required to echo it back, and doing so introduces a contract-drift surface without improving verification.
|
|
87
|
+
- Each `exports[]` entry is a dict with at minimum `name` (non-empty string) and `kind` (one of `function|class|type|constant|hook|interface|method|struct|enum|trait|macro|adapter`). The enum spans the constructs SKF actually documents across languages and skill types: JS/TS (`function`/`class`/`type`/`constant`/`hook`/`interface`/`method`), Rust public-API items (`struct`/`enum`/`trait`/`macro` — alongside the shared `type`/`constant`/`function`), and stack-composition scaffolds (`adapter`). Malformed entries are counted in `rejectedCount`.
|
|
88
|
+
- Each non-empty `cross_check_mismatches[]` entry carries `export`, `skill_md_line`, `reference_file`, `reference_line`, `issue`.
|
|
89
|
+
|
|
90
|
+
The script returns `{"valid": bool, "violations": [...], "rejectedCount": N, "exportsCount": N, "inventory": {...}|null}`. **If `valid` is false → HALT** `coverage-check: subagent inventory failed schema validation — {violations joined}` (do not downgrade to a warning; a grader must not trust malformed subagent output). When `valid` is true, consume the script's returned `inventory` object as the documented inventory for the spot-check and all downstream steps — do **not** re-parse the raw response by hand.
|
|
91
|
+
|
|
92
|
+
**Spot-check (ground-truth verification, zero-hallucination guard):** operate on the validated `inventory` from the script.
|
|
80
93
|
|
|
81
|
-
1. If `
|
|
82
|
-
2. Otherwise, sample `min(3,
|
|
83
|
-
3. For each sampled export, grep for the name across SKILL.md **and every reference file the subagent listed in
|
|
94
|
+
1. If `inventory.exports` is empty (`exportsCount == 0`): skip the spot-check (no names to verify). Zero-exports policy is handled in the §2b zero-exports guard.
|
|
95
|
+
2. Otherwise, sample `min(3, exportsCount)` exports deterministically — by default take indices `[0, len//2, len-1]` (first, middle, last) from `inventory.exports` after a stable sort by `name`.
|
|
96
|
+
3. For each sampled export, grep for the name across SKILL.md **and every reference file the subagent listed in `inventory.references`** (the documented surface of a split-body skill spans both): `grep -n "{export.name}" {resolved_skill_package}/SKILL.md {resolved_skill_package}/{each references[] path}` in the parent context. The name must appear at least once somewhere in that file set. Greping SKILL.md alone would false-HALT a split-body skill whose sampled export is documented only in a `references/*.md` file (a legitimate placement per §1 step 2 and the split-body note below).
|
|
84
97
|
4. If a sampled name returns zero matches across SKILL.md **and** all listed reference files, HALT "coverage-check: subagent inventory failed ground-truth spot-check — `{name}` claimed as export but absent from SKILL.md and the listed reference files".
|
|
85
98
|
|
|
86
99
|
These checks catch two hallucination classes: schema-shape drift (subagent paraphrased or dropped the contract) and fabricated exports (subagent invented names not in the document). Both are disqualifying for a grader skill — do not downgrade to a warning.
|
|
@@ -91,7 +104,7 @@ These checks catch two hallucination classes: schema-shape drift (subagent parap
|
|
|
91
104
|
|
|
92
105
|
**Only execute if the subagent's `references` array is non-empty** (detected during split-body traversal in Section 1). Skip silently otherwise.
|
|
93
106
|
|
|
94
|
-
The subagent has already read both SKILL.md body and `references/*.md` files. For each function, class, type, or interface that appears in
|
|
107
|
+
The subagent has already read both SKILL.md body and `references/*.md` files. For each function, class, type, or interface that appears in both the SKILL.md body AND any `references/*.md` file, instruct the subagent (or perform in the same subagent call from Section 1) to compare the documented signatures and include mismatches in its JSON output as a `cross_check_mismatches` array:
|
|
95
108
|
|
|
96
109
|
- **Parameters:** name, type, order, optionality
|
|
97
110
|
- **Return types:** exact type match
|
|
@@ -136,7 +149,7 @@ Start from the package entry point (see 0b) and identify the public API surface.
|
|
|
136
149
|
For EACH source file that defines public API exports, delegate to a subagent that:
|
|
137
150
|
1. Uses ast-grep to extract all exported symbols with their full signatures (the `source_sig`)
|
|
138
151
|
2. Matches each export against the `documented_signatures` map supplied by the parent, comparing params (name, type, order, optionality) and return type
|
|
139
|
-
3. Returns
|
|
152
|
+
3. Returns only the JSON object below — no prose, no commentary, no markdown fences:
|
|
140
153
|
|
|
141
154
|
```json
|
|
142
155
|
{
|
|
@@ -167,7 +180,7 @@ Parent strips wrapping markdown fences (if present) before parsing, same as §1a
|
|
|
167
180
|
|
|
168
181
|
After the source-code analysis (§2) completes, compute `total_exports` — the count of exports discovered in the source / provenance-map / metadata.json, per the stratified-scope and State 2 rules resolved in §4.
|
|
169
182
|
|
|
170
|
-
**Stack-skill branch (`metadata.json.skill_type == "stack"`):** A stack skill's own barrel is empty by design — it composes constituent skills rather than exporting a proprietary surface — so `total_exports` derived from its own barrel is `0` for a *correctly* built stack, and its `[from skill: …]` citations never trip §0's `[EXT:…]`-only docs-only trigger. The zero-exports HALT below targets individual source-based skills and must
|
|
183
|
+
**Stack-skill branch (`metadata.json.skill_type == "stack"`):** A stack skill's own barrel is empty by design — it composes constituent skills rather than exporting a proprietary surface — so `total_exports` derived from its own barrel is `0` for a *correctly* built stack, and its `[from skill: …]` citations never trip §0's `[EXT:…]`-only docs-only trigger. The zero-exports HALT below targets individual source-based skills and must not fire for stacks. Derive the stack's coverage denominator (`stack_denominator`) from its composition surface, in priority order, and use it as `total_exports` for the rest of coverage scoring:
|
|
171
184
|
|
|
172
185
|
1. Provenance-map cited-contract count — when `{forge_data_folder}/{skill_name}/provenance-map.json` exists **and its `entries[]` is non-empty**: count the named cited contracts, **excluding entries whose `export_name` contains `::`** (impl-block methods roll up under an already-counted type). Use the same exclusion as §4b's named-export rule so the §2b and §4b stack denominators agree.
|
|
173
186
|
2. Otherwise the composition surface from `metadata.json`: `len(libraries) + len(integration_pairs)`.
|
|
@@ -197,21 +210,45 @@ Do not write the Coverage Analysis section. Do not proceed to scoring. This is a
|
|
|
197
210
|
|
|
198
211
|
### 2c. Reconcile Documented vs Source Surface (Deterministic Intersection)
|
|
199
212
|
|
|
200
|
-
On a split-body skill the §1 inventory (documented surface) and the §2 AST output (source barrel) are two independent lists, so the `Documented` count must be their **intersection**, not a parent estimate.
|
|
213
|
+
On a split-body skill the §1 inventory (documented surface) and the §2 AST output (source barrel) are two independent lists, so the `Documented` count must be their **intersection**, not a parent estimate. That reconciliation — set intersection/difference/cardinality plus the grep-verified numerator of the scalar/stack branches — is deterministic arithmetic with one correct answer per input, so it is performed by `uv run {reconcileScript}`, not by hand.
|
|
214
|
+
|
|
215
|
+
**Which branch applies — and therefore which `denominatorSource` the script runs — is the policy decision made here.** The denominator itself is resolved by §4/§4b; the script consumes the *already-resolved* denominator and does only the counting (`documented_set` is derived inside the script from the §1 `exports[]`, de-duplicated and with `kind: "method"` excluded — methods are members of an already-counted class/type, not top-level barrel exports). Pick exactly one branch:
|
|
216
|
+
|
|
217
|
+
1. **Enumerated path (`denominatorSource: "barrel"`)** — the common split-body case. `barrel_set` is the union of `exports_found[]` across every §2 per-file result. **When a stratified-scope or State-2 denominator applies (see §4) and it resolves to an enumerated name set** — the priority-2/3 re-derivation from `scope.tier_a_include` / `scope.include` globs — pass that resolved name set as `barrelSet` so the script intersects against it instead of the raw per-file union. The script computes `Documented := |documented_set ∩ barrel_set|`, `Missing := barrel_set − documented_set` (in source, not documented), `Stale := documented_set − barrel_set` (documented, not in source), and `Export Coverage = |Documented| / |barrel_set| * 100`.
|
|
218
|
+
|
|
219
|
+
2. **Scalar-denominator branch (`denominatorSource: "scalar"`)** — §4 priority 1, when the resolved denominator is the scalar `metadata.json.stats.effective_denominator` (a count with **no enumerated name set**). There is no `barrel_set` to intersect, so the script instead greps each `documented_set` name across `SKILL.md ∪ references/*.md` and counts appearances. Pass `denominatorValue: effective_denominator` and `skillPackagePath: {resolved_skill_package}`; the script sets `Missing := effective_denominator − Documented` and returns an empty `Stale` (not enumerable without a barrel name set). If §4b's numerator-ground-truth arm fires (it triggers only when `exports_documented == effective_denominator`), its verified count is authoritative and **overrides** this numerator — do not apply both.
|
|
220
|
+
|
|
221
|
+
3. **Stack-skill branch (`denominatorSource: "stack"`, `metadata.json.skill_type == "stack"`)** — a stack's source barrel is empty by design, so intersecting against it would divide by zero. Pass `denominatorValue: stack_denominator` (the §2b composition-surface denominator) and `compositionNames`: the provenance-map cited-contract names (`::`-excluded) when the map exists and is non-empty, else the `libraries` and `integration_pairs` names. The script greps each composition name across `SKILL.md ∪ references/*.md` for the numerator, sets `Missing := stack_denominator − Documented`, and omits `Stale` (no source barrel to enumerate against).
|
|
201
222
|
|
|
202
|
-
**
|
|
223
|
+
**Build the reconciliation input and run the script:**
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
echo '<JSON>' | uv run {reconcileScript} --stdin
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Input JSON (one object — supply only the fields the chosen branch needs; `{reconcileScript}` is resolved relative to the skill root):
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"denominatorSource": "barrel | scalar | stack",
|
|
234
|
+
"exports": [ /* §1 inventory exports[] — used for barrel + scalar; kind:"method" entries are excluded automatically */ ],
|
|
235
|
+
"barrelSet": [ /* barrel: resolved enumerated name set, when §4 supplies one */ ],
|
|
236
|
+
"perFileResults": [ /* barrel: the §2 per-file results, unioned into barrel_set when no barrelSet */ ],
|
|
237
|
+
"denominatorValue": 0,
|
|
238
|
+
"compositionNames": [ /* stack: composition-surface names to grep */ ],
|
|
239
|
+
"skillPackagePath": "{resolved_skill_package}"
|
|
240
|
+
}
|
|
241
|
+
```
|
|
203
242
|
|
|
204
|
-
|
|
205
|
-
2. **`barrel_set`** := the union of `exports_found[]` across every §2 per-file result (the actual source public surface). When a stratified-scope or State-2 denominator applies (see §4) **and it resolves to an enumerated name set** — the priority-2/3 re-derivation from `scope.tier_a_include` / `scope.include` globs — `barrel_set` is that resolved denominator's name set instead of the raw union.
|
|
243
|
+
The script returns (read these — **do not re-derive them by hand**):
|
|
206
244
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
4. Carry the resulting counts into §3's table and §4's Export Coverage: `Export Coverage = |Documented| / |barrel_set| * 100` (enumerated path) or `Documented / effective_denominator * 100` (scalar-denominator branch). Record the counts in the Coverage Analysis section so the numerator is auditable.
|
|
245
|
+
- `documented` — the numerator (`|documented_set ∩ barrel_set|` for barrel; grep-verified count for scalar/stack)
|
|
246
|
+
- `missing` / `missingCount` — source names not documented (barrel enumerates the names; scalar/stack give only the residual count)
|
|
247
|
+
- `stale` / `staleCount` — documented names not in source (barrel only; empty with `staleApplicable: false` for scalar/stack)
|
|
248
|
+
- `denominator` — `|barrel_set|` (barrel) or the resolved scalar/stack denominator
|
|
249
|
+
- `exportCoverage` — `documented / denominator * 100`, already rounded
|
|
213
250
|
|
|
214
|
-
|
|
251
|
+
Carry these into §3's table/summary and §4's Export Coverage, and record the counts in the Coverage Analysis section (§5) so the numerator is auditable. The `exportCoverage` recorded here is the value step 5 feeds to `compute-score.py` — it is the script's value, not a parent estimate.
|
|
215
252
|
|
|
216
253
|
### 3. Build Coverage Results
|
|
217
254
|
|
|
@@ -223,29 +260,22 @@ Aggregate findings across all source files:
|
|
|
223
260
|
|--------|------|-----------|-----------------|-----------|--------|
|
|
224
261
|
| {name} | function/class/type | yes/no | yes/no/unverified | src/file.ts:42 | PASS/FAIL/WARN |
|
|
225
262
|
|
|
226
|
-
**Summary counts** (from the §2c reconciliation — not re-estimated here):
|
|
227
|
-
- Total exports in source:
|
|
228
|
-
- Documented in SKILL.md: `
|
|
229
|
-
- Missing documentation:
|
|
263
|
+
**Summary counts** (read from the §2c reconciliation script's JSON — not re-estimated here):
|
|
264
|
+
- Total exports in source: `denominator`
|
|
265
|
+
- Documented in SKILL.md: `documented`
|
|
266
|
+
- Missing documentation: `missingCount`
|
|
230
267
|
- Signature mismatches: {N}
|
|
231
|
-
- Undocumented in SKILL.md but not in source (stale docs):
|
|
268
|
+
- Undocumented in SKILL.md but not in source (stale docs): `staleCount`
|
|
232
269
|
|
|
233
270
|
### 4. Load Scoring Rules
|
|
234
271
|
|
|
235
272
|
Load `{scoringRulesFile}` to determine category scores:
|
|
236
273
|
|
|
237
|
-
- **Export Coverage:** (documented /
|
|
274
|
+
- **Export Coverage:** the `exportCoverage` value returned by the §2c reconciliation script (`documented / denominator * 100`) — read it from the script's JSON, do not re-compute it here
|
|
238
275
|
- **Signature Accuracy:** (matching_signatures / total_documented) * 100 (Forge/Deep only, "N/A" for Quick)
|
|
239
276
|
- **Type Coverage:** (documented_types / total_types) * 100 (Forge/Deep only, "N/A" for Quick)
|
|
240
277
|
|
|
241
|
-
**
|
|
242
|
-
|
|
243
|
-
1. **Prefer `metadata.json.stats.effective_denominator`** when present. Use it directly as `total_exports` — but apply the **denominator deflation guard** from `{sourceAccessProtocol}` stratified-scope resolution step 1: when source is readable, re-derive the source barrel and, if it exceeds `effective_denominator` by >25% with no `scope.tier_a_include`, treat the stored value as deflated, use the re-derived count, and emit the `denominator deflation` gap.
|
|
244
|
-
2. **Otherwise re-derive at test time** from the brief's scope globs per the protocol. When the brief supplies `scope.tier_a_include`, re-derive from that narrower list (excluding umbrella barrel files per the protocol's umbrella-barrel note); otherwise re-derive from `scope.include`. Use the resulting union count as `total_exports`.
|
|
245
|
-
3. **Run the denominator inflation check** defined in `{sourceAccessProtocol}` stratified-scope resolution step 3 whenever re-derivation fell back to `scope.include`. If the `scope.include` union exceeds the provenance-map entry count by more than 25%, emit the Medium-severity `denominator inflation — coarse scope.include union exceeds authored surface` gap and append it to the Coverage Analysis gap list.
|
|
246
|
-
4. **Apply provenance-map canonicalization** before intersecting documented exports against the raw provenance-map entry list — see `{sourceAccessProtocol}` §Source API Surface Definition → "Provenance-map canonicalization" for the folding rules (`_def`/`_exact` suffix, `a11y_` prefix, renderer-prefix disambiguation). Skip folding when `metadata.json.stats.effective_denominator` is present and already equals the raw provenance-map entry count. Record the fold summary in the Coverage Analysis section so it's auditable.
|
|
247
|
-
|
|
248
|
-
Record the denominator source in the Coverage Analysis section as `Denominator: stratified ({effective_denominator | tier_a_include union | scope.include union}, {N} files matched)`. When stratified scope does not apply, use the standard barrel-based denominator and omit the stratified annotation.
|
|
278
|
+
**Resolve the coverage denominator per `{sourceAccessProtocol}` (already loaded in §0b) — do not re-derive its ladders here.** Determine which §Source API Surface Definition clause matches this skill and apply that clause exactly as written: **stratified-scope** (monorepo curated subset), **multi-entry (exports-map)**, **specific-modules**, **pattern-reference**, or the **State 2** provenance-vs-metadata cross-reference (union on divergence). Each clause fixes its own resolution priority (prefer `metadata.json.stats.effective_denominator` → `scope.tier_a_include` → `scope.include` / subpath union), its deflation and inflation guards, the umbrella-barrel exclusion, and provenance-map canonicalization (including the fold summary the canonicalization records). Use the clause's resolved value as `total_exports`; when no clause matches, use the standard barrel-based denominator. Record the denominator source in the Coverage Analysis section using the exact `Denominator: {barrel | stratified (…) | multi-entry (…) | specific-modules (…) | pattern-reference (…)}` annotation string the matching clause specifies.
|
|
249
279
|
|
|
250
280
|
**Record the two non-chosen candidate values alongside the chosen one.**
|
|
251
281
|
Stratified-scope resolution picks ONE of three denominator candidates
|
|
@@ -260,13 +290,15 @@ as-observed (or `absent` when the candidate was not present for this skill):
|
|
|
260
290
|
- `stats.effective_denominator`: {N | absent} {← chosen if priority (1) applied}
|
|
261
291
|
- `scope.tier_a_include` union: {N | absent} {← chosen if priority (2) applied}
|
|
262
292
|
- `scope.include` union: {N | absent} {← chosen if priority (3) applied}
|
|
293
|
+
- exports-map subpath union: {N | absent} {← chosen if the multi-entry clause applied}
|
|
294
|
+
- root barrel: {N} {secondary candidate — root-barrel-vs-subpath-union audit}
|
|
263
295
|
```
|
|
264
296
|
|
|
265
297
|
Readers can then spot-check whether the chosen denominator is reasonable
|
|
266
298
|
against the other two without re-running the extraction. A future reviewer who
|
|
267
|
-
suspects denominator gaming has the evidence inline.
|
|
268
|
-
|
|
269
|
-
|
|
299
|
+
suspects denominator gaming has the evidence inline. The `multi-entry` clause
|
|
300
|
+
requires the root-barrel named-export count in the `root barrel` row so the
|
|
301
|
+
root-barrel-vs-subpath-union choice is auditable.
|
|
270
302
|
|
|
271
303
|
### 4b. Metadata Export-Count Coherence Cross-Check
|
|
272
304
|
|
|
@@ -286,27 +318,49 @@ After the denominator has been resolved (standard, stratified, or State 2), cros
|
|
|
286
318
|
**Cluster B — documented surface** (what was extracted and documented, including methods and submodule members):
|
|
287
319
|
|
|
288
320
|
3. `metadata.json.stats.exports_documented` — the declared documented count
|
|
289
|
-
4. Provenance-map **named-export count** (if `{forge_data_folder}/{skill_name}/provenance-map.json` exists) —
|
|
290
|
-
5. `confidence_distribution`
|
|
321
|
+
4. Provenance-map **named-export count** (if `{forge_data_folder}/{skill_name}/provenance-map.json` exists) — pass the raw `export_name` values as `provenanceExportNames`; the script counts top-level named exports, **excluding entries whose `export_name` contains `::`** (impl-block methods like `Type::method`, which roll up under an already-counted type and are not separate barrel exports). Comparing the raw entry count instead false-positives on any method-enumerating provenance map (common for Rust/TS type-heavy skills): e.g. a map with 88 named exports + 48 `Type::method` entries reports 136 against `exports_documented` ≈ 92 → a spurious ~32% Cluster-B drift, while the comparable named-export count (88) agrees within ~4%.
|
|
322
|
+
5. `confidence_distribution` (`t1`, `t1_low`, `t2`, `t3`, when present in `metadata.json.stats`) — pass the tiers as `confidenceDistribution`; the script sums them. Every extracted/documented export is binned into exactly one confidence tier, so the distribution must sum to the documented-surface total; a divergence (e.g., distribution sums to 91 while `exports_documented` is 85) is an internal-consistency defect even when the two clusters look fine
|
|
291
323
|
|
|
292
324
|
Cluster assignment is canonical: `skf-create-skill` step 5 derives `exports_public_api` from entry-point validation and writes the `exports[]` array from the same barrel surface (see `skf-create-skill/references/compile.md:105`), while `exports_documented` tracks the broader documented surface that the provenance-map also enumerates.
|
|
293
325
|
|
|
294
|
-
**
|
|
326
|
+
**Delegate the drift arithmetic to `uv run {coherenceScript}`.** The intra-cluster / cross-cluster `>10%` comparisons are deterministic count arithmetic with one correct answer per input, so the script — not the prompt — owns the binning, the divergence percentages, and every skip condition (a cluster with fewer than two present counts, and clusters that agree within the threshold, are skipped inside the script). Build the input from the counts collected above and run it (`{coherenceScript}` resolves relative to the skill root):
|
|
295
327
|
|
|
296
|
-
|
|
328
|
+
```bash
|
|
329
|
+
echo '<JSON>' | uv run {coherenceScript} --stdin
|
|
330
|
+
```
|
|
297
331
|
|
|
298
|
-
|
|
332
|
+
Input JSON (omit any count that is absent; `driftThresholdPct` defaults to `10`):
|
|
299
333
|
|
|
300
|
-
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"skillType": "{metadata.json.skill_type or null}",
|
|
337
|
+
"scopeType": "{metadata.json.scope_type or null}",
|
|
338
|
+
"clusterA": {"exports_public_api": 0, "exports_length": 0},
|
|
339
|
+
"clusterB": {"exports_documented": 0},
|
|
340
|
+
"provenanceExportNames": [ /* raw provenance-map export_name values — the script excludes `::` impl-block methods and counts the rest */ ],
|
|
341
|
+
"confidenceDistribution": {"t1": 0, "t1_low": 0, "t2": 0, "t3": 0}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
301
344
|
|
|
302
|
-
|
|
345
|
+
The script returns `{"skipped": bool, "clusterACounts": {...}, "clusterBCounts": {...}, "findings": [...]}`. Read `findings[]` and append each entry directly — **do not re-derive the percentages by hand.** Each entry carries `severity`, `title`, `detail` (the enumerated counts + drift %), and `category: structural/metadata coherence`:
|
|
303
346
|
|
|
304
|
-
|
|
347
|
+
- A **Medium** `metadata drift — {barrel|documented-surface} export counts diverge` is the real drift signal — the two sources should mirror the same surface and they don't, so upstream extraction or compilation produced inconsistent output that a re-compile should reconcile. It is classified under structural/metadata coherence regardless of naive/contextual mode.
|
|
348
|
+
- An **Info** `multi-denominator reporting — barrel vs documented surface` is expected for skills whose documented surface intentionally exceeds the barrel (methods, submodule members, re-exported classes) — it is not drift. The note exists so the test report makes the dual-denominator design visible and auditable without demanding action.
|
|
305
349
|
|
|
306
|
-
-
|
|
307
|
-
- If verified < declared, emit a **High**-severity gap `numerator inflation — {declared − verified} of {declared} declared exports absent from SKILL.md/references` listing the absent names, and use the verified count as the Export Coverage numerator (overriding `exports_documented`). A numerator padded to equal the denominator otherwise produces a tautological 100% that passes the gate.
|
|
350
|
+
(The stack and reference-app branches above already skip this delegation; the script also returns `skipped: true` when passed their `skillType` / `scopeType`, so an unconditional call stays safe.)
|
|
308
351
|
|
|
309
|
-
|
|
352
|
+
**Numerator ground-truth — force a full grep on the inflation signature.** The intra/cross-cluster checks above only compare *counts*; they cannot tell whether the declared exports actually appear in the skill. When `metadata.json.stats.exports_documented == effective_denominator` exactly (numerator equals denominator — the signature of a numerator padded to match the full surface), do not trust the documented count: grep the full declared set (the `metadata.exports[]` / provenance-map names, not the §1a 3-sample) against `SKILL.md ∪ references/*.md`. That grep + present/absent set-diff + count has one correct answer per input, so it runs in `uv run {numeratorVerifyScript}`, not in-prompt (`{numeratorVerifyScript}` resolves relative to the skill root):
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
echo '{"declaredNames": [ /* full declared set */ ], "skillPackagePath": "{resolved_skill_package}"}' | uv run {numeratorVerifyScript} --stdin
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Read the script's output — do not re-derive it by hand:
|
|
359
|
+
|
|
360
|
+
- `inflated: false` (`verified == declared`) → the skill is genuinely fully documented; no finding, coverage stands.
|
|
361
|
+
- `inflated: true` (`verified < declared`) → emit a **High**-severity gap `numerator inflation — {declared − verified} of {declared} declared exports absent from SKILL.md/references` listing the script's `absent[]` names, and use `verified` as the Export Coverage numerator (overriding `exports_documented`). A numerator padded to equal the denominator otherwise produces a tautological 100% that passes the gate.
|
|
362
|
+
|
|
363
|
+
The grep runs only on the exact-equality signature, so it adds no cost to the common case where the numerator is already below the denominator. Unlike the count-coherence findings above, this arm is authoritative — it changes the numerator used for scoring.
|
|
310
364
|
|
|
311
365
|
Append any findings (Medium gaps, the Info note, and/or the High numerator-inflation gap) to the Coverage Analysis section's gap list (built in section 5) so they surface in the final test report alongside coverage and signature findings. The count-coherence findings are informational about data quality and do not change the denominator chosen above; the numerator ground-truth arm is the one exception that overrides the numerator.
|
|
312
366
|
|
|
@@ -350,17 +404,7 @@ Note: Weight application is deferred to step 5 where all category weights are ca
|
|
|
350
404
|
|
|
351
405
|
### 6. Report Coverage Results
|
|
352
406
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
**{skill_name}** — {forge_tier} tier analysis of {file_count} source files:
|
|
356
|
-
|
|
357
|
-
- Exports: {documented}/{total} documented ({percentage}%)
|
|
358
|
-
- Signatures: {matching}/{total} accurate ({percentage}% or N/A for Quick)
|
|
359
|
-
- Types: {documented_types}/{total_types} covered ({percentage}% or N/A for Quick)
|
|
360
|
-
|
|
361
|
-
**{N} issues found** — details in Coverage Analysis section.
|
|
362
|
-
|
|
363
|
-
**Proceeding to coherence check...**"
|
|
407
|
+
Report the coverage result to the user: the {forge_tier}-tier analysis of {file_count} source files, the documented ratios for exports / signatures / types (signatures and types are N/A for Quick tier), and the issue count — full details are in the Coverage Analysis section. Then proceed to the coherence check.
|
|
364
408
|
|
|
365
409
|
Update stepsCompleted, then load and execute {nextStepFile}.
|
|
366
410
|
|
|
@@ -11,41 +11,19 @@ outputFile: '{forge_version}/test-report-{skill_name}-{run_id}.md'
|
|
|
11
11
|
|
|
12
12
|
Examine the skill metadata to determine whether this is an individual skill (naive mode — API surface coverage only) or a stack skill (contextual mode — full coherence validation including cross-references and integration patterns).
|
|
13
13
|
|
|
14
|
-
### 1.
|
|
14
|
+
### 1. Determine Test Mode
|
|
15
15
|
|
|
16
|
-
Read the skill metadata (loaded in step 01) and
|
|
16
|
+
Read the skill metadata (loaded in step 01) and branch on its `skill_type` field — a single deterministic lookup:
|
|
17
17
|
|
|
18
|
-
**
|
|
19
|
-
- `skill_type: '
|
|
20
|
-
-
|
|
21
|
-
- No cross-references to other skills
|
|
22
|
-
- Self-contained API surface
|
|
18
|
+
- `skill_type: 'single'` → **Naive Mode** (API-surface coverage; coherence is structural only, no coherence category in scoring)
|
|
19
|
+
- `skill_type: 'stack'` → **Contextual Mode** (full coherence validation — cross-references resolve, types match, integration patterns complete; coherence category scored)
|
|
20
|
+
- unset or unclear → default to **Naive Mode** (conservative — fewer checks, less chance of false negatives from missing context) and note the default in the report
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
- `skill_type: 'stack'` in metadata
|
|
26
|
-
- References multiple skills or packages
|
|
27
|
-
- Contains cross-references in SKILL.md
|
|
28
|
-
- Integration patterns documented between components
|
|
22
|
+
What each mode actually checks and how category weights are distributed is owned by `scoring-rules.md` (Tier-Dependent Scoring) and `coherence-check.md` — do not restate it here.
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
**Quick-tier adjustment (applies to both modes):** If `forge_tier` is `Quick`, Signature Accuracy and Type Coverage are skipped during scoring (no AST available); their weights are redistributed proportionally to the remaining active categories per `scoring-rules.md` Tier-Dependent Scoring.
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
- Coverage check: documented exports vs source API surface
|
|
34
|
-
- Coherence check: basic structural validation only (no cross-references to verify)
|
|
35
|
-
- Scoring: redistributed weights (no coherence category)
|
|
36
|
-
|
|
37
|
-
**IF stack skill → Contextual Mode:**
|
|
38
|
-
- Coverage check: documented exports vs source API surface (same as naive)
|
|
39
|
-
- Coherence check: full validation — cross-references exist, types match, integration patterns complete
|
|
40
|
-
- Scoring: full category weights including coherence
|
|
41
|
-
|
|
42
|
-
**IF metadata unclear or skill_type not set:**
|
|
43
|
-
- Default to **naive mode** (conservative — fewer checks, less chance of false negatives from missing context)
|
|
44
|
-
- Note the default in the report
|
|
45
|
-
|
|
46
|
-
**Quick-tier adjustment (applies to both modes):** If `forge_tier` is `Quick`, Signature Accuracy and Type Coverage are skipped during scoring (no AST available). Their weights are redistributed proportionally to remaining active categories. See `scoring-rules.md` Tier-Dependent Scoring section for details.
|
|
47
|
-
|
|
48
|
-
### 3. Update Output Document
|
|
26
|
+
### 2. Update Output Document
|
|
49
27
|
|
|
50
28
|
Update `{outputFile}` frontmatter:
|
|
51
29
|
- Set `testMode: '{naive|contextual}'`
|
|
@@ -66,18 +44,9 @@ Append the **Test Summary** section to `{outputFile}`:
|
|
|
66
44
|
- Coherence Check: {what will be checked based on mode + tier}
|
|
67
45
|
```
|
|
68
46
|
|
|
69
|
-
###
|
|
70
|
-
|
|
71
|
-
"**Mode detected: {NAIVE|CONTEXTUAL}**
|
|
72
|
-
|
|
73
|
-
**{skill_name}** is {an individual skill / a stack skill}, so we'll run in **{naive/contextual}** mode.
|
|
74
|
-
|
|
75
|
-
**What this means:**
|
|
76
|
-
- Coverage: {brief description of coverage scope}
|
|
77
|
-
- Coherence: {brief description of coherence scope}
|
|
78
|
-
- Scoring: {which weight distribution applies}
|
|
47
|
+
### 3. Report Mode Detection
|
|
79
48
|
|
|
80
|
-
|
|
49
|
+
Report the detected mode ({naive|contextual}) and why it was selected (individual skill → naive, stack → contextual), then proceed to the coverage check.
|
|
81
50
|
|
|
82
51
|
Update stepsCompleted, then load and execute {nextStepFile}.
|
|
83
52
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'step-hard-gate.md'
|
|
3
3
|
outputFile: '{forge_version}/test-report-{skill_name}-{run_id}.md'
|
|
4
|
+
externalScoreScript: 'scripts/combine-external-scores.py'
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -25,7 +26,7 @@ Before running external validators, check if `{forge_data_folder}/{skill_name}/e
|
|
|
25
26
|
- If the command fails (exit code non-zero) or git is not available, the file is either **untracked** (new, never committed) or we're in a **non-git environment**:
|
|
26
27
|
- Check if `{skillDir}/metadata.json` exists and has a `generation_date` field
|
|
27
28
|
- Compare `metadata.json` `generation_date` against the evidence report's generation date (from its frontmatter `generated` field or the `## Validation Results` timestamp)
|
|
28
|
-
- **Precision guard (mirror of the git-path Primary-cross check):** date-granularity equality is
|
|
29
|
+
- **Precision guard (mirror of the git-path Primary-cross check):** date-granularity equality is not proof of same-session generation. A same-day `update-skill` that regenerates SKILL.md *after* the cached evidence report was produced yields the same calendar date (e.g. `metadata.generation_date: 2026-05-23T00:00:00Z` vs evidence `generated: 2026-05-23`), so reusing on date-equality alone would publish pre-update scores for post-update content. Auto-reuse is safe **only** when both timestamps carry a real time-of-day component — neither a date-only string (`2026-05-23`) nor a midnight-coerced `…T00:00:00Z` — AND they match to the minute. In that case auto-reuse: the evidence report was generated from the same SKILL.md content.
|
|
29
30
|
- Otherwise — if either timestamp is date-only or midnight-coerced, if they differ, or if `metadata.json` is missing or has no `generation_date` — treat as stale and proceed to section 2 for a fresh run. Forcing a fresh run on ambiguous precision matches the git path's bias toward freshness over reusing possibly-stale scores.
|
|
30
31
|
- Note: "Staleness check: SKILL.md is untracked/non-git — using metadata.json timestamp comparison (date-only/midnight timestamps force a fresh run)."
|
|
31
32
|
- If the command succeeds (file is tracked by git), continue to Primary check below.
|
|
@@ -102,7 +103,7 @@ timeout 120s npx --no-install -y tessl skill review {skillDir}
|
|
|
102
103
|
|
|
103
104
|
Timeout handling mirrors skill-check: exit `124` → `tessl_score: N/A` with reason `timeout-120s`. If the percentage regex (`/(Description|Content|Review Score):\s*(\d+)%/`) returns fewer than three matches, record `tessl_score: N/A` with reason `parse-failure` and include the first 200 chars of output in evidence-report for debugging.
|
|
104
105
|
|
|
105
|
-
**Registry-404 branch:** if the invocation emits `npm error 404 Not Found` or the npx wrapper exits with a not-found condition, record `tessl_score: N/A` with reason `pin-not-on-registry` and continue.
|
|
106
|
+
**Registry-404 branch:** if the invocation emits `npm error 404 Not Found` or the npx wrapper exits with a not-found condition, record `tessl_score: N/A` with reason `pin-not-on-registry` and continue. tessl has historically shipped under shifting scope/tag combinations, so a missing registry entry does not HALT the workflow.
|
|
106
107
|
|
|
107
108
|
**Parse the output** to extract:
|
|
108
109
|
- `description_score` — percentage (e.g., 100%)
|
|
@@ -123,17 +124,18 @@ Store in context: `tessl_description_score`, `tessl_content_score`, `tessl_revie
|
|
|
123
124
|
|
|
124
125
|
### 4. Calculate Combined External Score
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
The combined external score feeds `externalValidation` into the scoring script (step 5), so its mean is computed by a script, not in-prompt. Both scores are on the same 0-100 scale (skill-check quality score; tessl review percentage). Pass each score, or `null` when its tool did not run or returned N/A (`{externalScoreScript}` resolves relative to the skill root):
|
|
127
128
|
|
|
128
|
-
```
|
|
129
|
-
|
|
129
|
+
```bash
|
|
130
|
+
echo '{"skillCheckScore": <score or null>, "tesslReviewScore": <score or null>}' | uv run {externalScoreScript} --stdin
|
|
130
131
|
```
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
Read the result — do not re-average by hand:
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
- `externalScore` — the combined score: the mean when both tools ran, the single score when one ran, or `null` when neither ran (the scoring step redistributes the external-validation weight on `null`).
|
|
136
|
+
- `toolsUsed[]` — the tools that contributed.
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
Record `external_score: N/A` when `externalScore` is `null`.
|
|
137
139
|
|
|
138
140
|
### 5. Append External Validation to Output
|
|
139
141
|
|
|
@@ -165,17 +167,7 @@ Append to `{outputFile}`:
|
|
|
165
167
|
|
|
166
168
|
### 6. Report Results
|
|
167
169
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
| Tool | Score | Status |
|
|
171
|
-
|------|-------|--------|
|
|
172
|
-
| skill-check | {score}/100 | {available/skipped} |
|
|
173
|
-
| tessl | {average}% | {available/skipped} |
|
|
174
|
-
| **Combined** | **{external_score}%** | |
|
|
175
|
-
|
|
176
|
-
{content quality warning if tessl content < 70%}
|
|
177
|
-
|
|
178
|
-
**Proceeding to scoring...**"
|
|
170
|
+
Report the external validation result to the user: the per-tool scores and availability (skill-check out of 100, tessl as a percentage average, each `available` or `skipped`), the combined external score, and a content-quality warning if tessl content is below 70%. Then proceed to scoring.
|
|
179
171
|
|
|
180
172
|
Update stepsCompleted, then load and execute {nextStepFile}.
|
|
181
173
|
|