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,14 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
shapeDetectProbeOrder:
|
|
3
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-shape-detect.py'
|
|
4
|
+
- '{project-root}/src/shared/scripts/skf-shape-detect.py'
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# Shape Detection Reference
|
|
2
8
|
|
|
3
9
|
Reference document for invoking `skf-shape-detect.py` — the shared shape classification module. Loaded by `step-auto-scope.md` for auto-scope analysis.
|
|
4
10
|
|
|
5
11
|
## Invocation Contract
|
|
6
12
|
|
|
7
|
-
**
|
|
13
|
+
**Resolve `{shapeDetectHelper}`** from `{shapeDetectProbeOrder}`; first existing path wins; HALT if neither resolves.
|
|
8
14
|
|
|
9
15
|
**Command:**
|
|
10
16
|
```
|
|
11
|
-
uv run
|
|
17
|
+
uv run {shapeDetectHelper} --repo-url <url> \
|
|
18
|
+
--manifests <path1,path2,...> \
|
|
19
|
+
--grammar-files <g1,g2,...> --tree-paths <d1/,d2/,file,...>
|
|
12
20
|
```
|
|
13
21
|
|
|
14
22
|
**Arguments:**
|
|
@@ -16,9 +24,11 @@ uv run python src/shared/scripts/skf-shape-detect.py --repo-url <url> --manifest
|
|
|
16
24
|
| Arg | Required | Description |
|
|
17
25
|
|-----|----------|-------------|
|
|
18
26
|
| `--repo-url` | Yes | Repository URL (context only — no cloning performed) |
|
|
19
|
-
| `--manifests` | Yes | Comma-separated local file paths to manifest files |
|
|
27
|
+
| `--manifests` | Yes | Comma-separated local file paths to manifest files (may be empty when a tree-level signal carries the classification) |
|
|
28
|
+
| `--grammar-files` | No | Comma-separated repo-relative grammar files (`*.y`, `*.g4`, `*.pest`, `Grammar/python.gram`, ...) — a whole-language signal |
|
|
29
|
+
| `--tree-paths` | No | Comma-separated repo-relative directory (trailing `/`) and structural file signals harvested from the clone (a `compiler/` dir, a lexer+parser+ast triad) |
|
|
20
30
|
|
|
21
|
-
**Supported manifests:** `package.json`, `pyproject.toml`, `Cargo.toml`
|
|
31
|
+
**Supported manifests:** `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `pom.xml`, `build.gradle`, `build.gradle.kts`, `Package.swift`
|
|
22
32
|
|
|
23
33
|
## Output Schema
|
|
24
34
|
|
|
@@ -49,22 +59,19 @@ On exit code 2, error details are written to stderr as JSON: `{"error": "message
|
|
|
49
59
|
| `library-API` | `full-library` | export_count ≤ 200 |
|
|
50
60
|
| `library-API` | `public-api` | export_count > 200 (surface too large for full coverage) |
|
|
51
61
|
| `reference-app` | `reference-app` | Direct mapping — apps, CLIs, demos |
|
|
52
|
-
| `language-reference` | `full-library` | Language tools/parsers are library-shaped from a skill perspective |
|
|
53
|
-
| `stack-compose` | `full-library` |
|
|
62
|
+
| `language-reference` | `full-library` | Language tools/parsers are library-shaped from a skill perspective. **Corpora-dependent** for a *whole-language* reference (a `grammar_file:`/`tree_triad:` signal — a compiler/interpreter): its value is the language's prose (guide/Book + std/library docs), not compiler internals, so step-auto-scope.md §6b seeds companion corpora and §6/§7 record an honest DEGRADED caveat when none are found (mirrors the §3b facet-coverage guard). A parser *library* (`parser_producer:`/`parser_dep:`) is exempt — its code is the product. |
|
|
63
|
+
| `stack-compose` | `full-library` | Decomposition candidate when `package_count > 3` — cohesion-checked in step-auto-scope.md §3b |
|
|
54
64
|
| `unknown` | N/A | Triggers fallback to interactive mode |
|
|
55
65
|
|
|
56
66
|
## Decomposition Thresholds
|
|
57
67
|
|
|
58
|
-
When auto-scope detects a
|
|
68
|
+
When auto-scope detects a multi-package monorepo, it may recommend multi-skill decomposition instead of producing one unwieldy skill. The threshold is evaluated in step-auto-scope.md §3a.
|
|
59
69
|
|
|
60
70
|
| Threshold | Value | Signal | Decomposition Path |
|
|
61
71
|
|-----------|-------|--------|-------------------|
|
|
62
|
-
|
|
|
63
|
-
| Multi-package / monorepo | `package_count > 3` `[PENDING VALIDATION]` | Shape detection `package_count` | One skill per workspace package |
|
|
64
|
-
|
|
65
|
-
Both thresholds are marked `[PENDING VALIDATION]` — no empirical data exists yet for real-world repos. Expected tuning: after running deepwiki against 5–10 real repos, adjust thresholds based on whether decomposition produces useful skill boundaries or noise.
|
|
72
|
+
| Multi-package / monorepo | `package_count > 3` | Shape detection `package_count` | Cohesion check (§3b): merge to one skill or split per package |
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
`package_count > 3` makes a **monorepo** a decomposition candidate; step-auto-scope.md §3b then decides merge-vs-split. It is empirically validated (fires on real 15-, 38-, and 442-package workspaces). A *single* package with a large API surface is **not** decomposed — it produces one cohesive skill that `skf-create-skill`'s auto-shard splits into `references/` shards at the 400-line ceiling.
|
|
68
75
|
|
|
69
76
|
When neither threshold is met, the single-scope flow proceeds unchanged.
|
|
70
77
|
|
|
@@ -72,7 +79,7 @@ When neither threshold is met, the single-scope flow proceeds unchanged.
|
|
|
72
79
|
|
|
73
80
|
The five-shape heuristic ladder applies in order (first match wins):
|
|
74
81
|
|
|
75
|
-
1. **language-reference** — parser/grammar/language-toolchain project. Signals: parser-
|
|
82
|
+
1. **language-reference** — parser/grammar/language-toolchain project. Signals, strongest first: a hand-written-compiler tree structure (a dedicated `compiler/` directory with a lexer+parser+ast triad plus a codegen/VM/type-checker member — catches rustc, TypeScript, Go); a declared grammar file (`Grammar/python.gram`, a root `parse.y`, a `*.g4` — catches CPython, Ruby); the repo's own name being a known parser/grammar tool (pest, lalrpop, lark — the producer); or a parser-generator dependency (a DSL built on antlr4/lalrpop — the consumer). Delegating consumers (formatters, linters, bundlers that depend on a parser) and markup/DSL parsers (CSS, markdown, GraphQL) are excluded.
|
|
76
83
|
2. **stack-compose** — multi-ecosystem composite project. Signals: manifests from 2+ distinct ecosystems
|
|
77
84
|
3. **reference-app** — application, CLI, or demo project. Signals: npm `bin` field, Rust `[[bin]]`, framework deps (next, fastapi, axum, etc.)
|
|
78
85
|
4. **library-API** — library exposing a programmatic API. Signals: `main`/`module`/`exports` fields, `[lib]` target, export count
|
|
@@ -77,7 +77,7 @@ Rules for identifying discrete skillable units within a project. A "skillable un
|
|
|
77
77
|
- Scope type: inherits from the dominant constituent (typically `full-library` or `specific-modules`)
|
|
78
78
|
|
|
79
79
|
**Detection heuristic (apply after initial classification, before user confirmation):**
|
|
80
|
-
1. Among the qualifying units, find groups of ≥2 boundaries where
|
|
80
|
+
1. Among the qualifying units, find groups of ≥2 boundaries where either:
|
|
81
81
|
- **Mutual hard dependency:** Every constituent imports from at least one other constituent in the group, AND no constituent's public API is self-contained (removing any one breaks the others)
|
|
82
82
|
- **Shared integration surface:** Constituents share types/traits defined in one constituent but consumed by all others, AND the consuming constituents have no independent barrel (their value depends on the shared definitions)
|
|
83
83
|
2. For each detected group, propose merging into a single composite unit:
|
|
@@ -88,7 +88,7 @@ Rules for identifying discrete skillable units within a project. A "skillable un
|
|
|
88
88
|
|
|
89
89
|
## Disqualification Rules
|
|
90
90
|
|
|
91
|
-
Do
|
|
91
|
+
Do not recommend a boundary as a skillable unit when:
|
|
92
92
|
|
|
93
93
|
1. **Too small**: Fewer than 3 source files or 100 lines of code
|
|
94
94
|
2. **Generated code**: Auto-generated files (protobuf, GraphQL codegen, etc.)
|
|
@@ -7,11 +7,12 @@ description: Drift detection between skill and current source code. Use when the
|
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
Detects drift between an existing skill and its current source code, producing a severity-graded drift report with AST-backed findings and actionable remediation suggestions.
|
|
10
|
+
Detects drift between an existing skill and its current source code, producing a severity-graded drift report with AST-backed findings and actionable remediation suggestions. Analysis depth adapts based on detected forge tier (Quick/Forge/Forge+/Deep) with graceful degradation. Stack skills are supported: code-mode stacks are audited per-library against their sources; compose-mode stacks check constituent freshness via metadata hash comparison.
|
|
11
11
|
|
|
12
12
|
## Conventions
|
|
13
13
|
|
|
14
14
|
- Bare paths (e.g. `references/<name>.md`) resolve from the skill root.
|
|
15
|
+
- **Module-level path exception:** bare paths beginning with `knowledge/` or `shared/` resolve from the SKF module root (`{project-root}/_bmad/skf/` installed, `src/` in dev), not the skill root — stage files reference `knowledge/version-paths.md` and `knowledge/tool-resolution.md`, and the terminal step chains to `shared/health-check.md`.
|
|
15
16
|
- `references/` holds prompt content carved out of SKILL.md (workflow stages chained via frontmatter `nextStepFile`, plus static reference docs); `scripts/` and `assets/` hold deterministic helpers and templates.
|
|
16
17
|
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).
|
|
17
18
|
- `{project-root}`-prefixed paths resolve from the project working directory.
|
|
@@ -19,7 +20,7 @@ Detects drift between an existing skill and its current source code, producing a
|
|
|
19
20
|
|
|
20
21
|
## Role
|
|
21
22
|
|
|
22
|
-
You are a skill auditor
|
|
23
|
+
You are a skill auditor in Ferris Audit mode: a deterministic drift-detection workflow where the source code is the ground truth and every finding traces back to it.
|
|
23
24
|
|
|
24
25
|
## Workflow Rules
|
|
25
26
|
|
|
@@ -56,7 +57,7 @@ These rules apply to every step in this workflow:
|
|
|
56
57
|
|
|
57
58
|
## Exit Codes
|
|
58
59
|
|
|
59
|
-
Every
|
|
60
|
+
Every hard halt in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
|
|
60
61
|
|
|
61
62
|
| Code | Meaning | Raised by |
|
|
62
63
|
| ---- | -------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
@@ -68,13 +69,13 @@ Every HARD HALT in this workflow exits with a stable code so headless automators
|
|
|
68
69
|
|
|
69
70
|
## Result Contract (Headless)
|
|
70
71
|
|
|
71
|
-
When `{headless_mode}` is true, step 6 emits a single-line JSON envelope on **stdout** before chaining to step 7, and every
|
|
72
|
+
When `{headless_mode}` is true, step 6 emits a single-line JSON envelope on **stdout** before chaining to step 7, and every hard halt emits the same envelope shape on **stderr** with `status: "error"`:
|
|
72
73
|
|
|
73
74
|
```
|
|
74
75
|
SKF_AUDIT_RESULT_JSON: {"status":"success|error","skill_name":"…","drift_score":"CLEAN|MINOR|SIGNIFICANT|CRITICAL|null","report_path":"…|null","next_workflow":"update-skill|null","audit_ref":"…|null","exit_code":0,"halt_reason":null}
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
`status` is `"success"` on the terminal happy path, `"error"` on any
|
|
78
|
+
`status` is `"success"` on the terminal happy path, `"error"` on any halt. `drift_score` is `null` when the workflow halted before severity classification ran. `next_workflow` is `"update-skill"` when CRITICAL or HIGH findings exist, otherwise `null`. `halt_reason` is one of: `null` (success), `"input-missing"`, `"skill-not-found"`, `"forge-tier-missing"`, `"source-dir-missing"`, `"write-failed"`, `"user-cancelled"`. `exit_code` matches the table above.
|
|
78
79
|
|
|
79
80
|
## On Activation
|
|
80
81
|
|
|
@@ -106,6 +107,8 @@ SKF_AUDIT_RESULT_JSON: {"status":"success|error","skill_name":"…","drift_score
|
|
|
106
107
|
- `{severityRulesPath}` ← `workflow.severity_rules_path` if non-empty, else `references/severity-rules.md`
|
|
107
108
|
- `{onCompleteCommand}` ← `workflow.on_complete` if non-empty, else empty (no-op — report.md skips the hook invocation entirely)
|
|
108
109
|
|
|
109
|
-
Stash all three as workflow-context variables. Stage files reference `{driftReportTemplatePath}` / `{severityRulesPath}` / `{onCompleteCommand}` directly
|
|
110
|
+
Stash all three as workflow-context variables. Stage files reference `{driftReportTemplatePath}` / `{severityRulesPath}` / `{onCompleteCommand}` directly.
|
|
111
|
+
|
|
112
|
+
Also apply the array surfaces (not silent no-ops): run `workflow.activation_steps_prepend` now, treat `workflow.persistent_facts` as standing context for the run (`file:`-prefixed entries load their file/glob contents as facts — the bundled default globs any `project-context.md`), then run `workflow.activation_steps_append` after activation.
|
|
110
113
|
|
|
111
114
|
4. Load, read the full file, and then execute `references/init.md` to begin the workflow.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# DO NOT EDIT -- overwritten on every update.
|
|
2
2
|
#
|
|
3
3
|
# Workflow customization surface for skf-audit-skill.
|
|
4
|
+
# Team overrides: _bmad/custom/skf-audit-skill.toml (under {project-root})
|
|
5
|
+
# Personal overrides: _bmad/custom/skf-audit-skill.user.toml (under {project-root})
|
|
4
6
|
|
|
5
7
|
[workflow]
|
|
6
8
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 're-index.md'
|
|
3
3
|
outputFile: '{forge_version}/drift-report-{timestamp}.md'
|
|
4
|
-
templateFile: '
|
|
4
|
+
templateFile: '{driftReportTemplatePath}'
|
|
5
5
|
loadProvenanceProbeOrder:
|
|
6
6
|
- '{project-root}/_bmad/skf/shared/scripts/skf-load-provenance.py'
|
|
7
7
|
- '{project-root}/src/shared/scripts/skf-load-provenance.py'
|
|
8
8
|
compareFileHashesProbeOrder:
|
|
9
9
|
- '{project-root}/_bmad/skf/shared/scripts/skf-compare-file-hashes.py'
|
|
10
10
|
- '{project-root}/src/shared/scripts/skf-compare-file-hashes.py'
|
|
11
|
+
compareConstituentHashesProbeOrder:
|
|
12
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-hash-content.py'
|
|
13
|
+
- '{project-root}/src/shared/scripts/skf-hash-content.py'
|
|
11
14
|
---
|
|
12
15
|
|
|
13
16
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -52,9 +55,9 @@ Which skill would you like to audit? Please provide the skill name or path."
|
|
|
52
55
|
|
|
53
56
|
- **[N] Audit symlink target ({symlink_target})** — recommended. The drift report describes the version the skill currently resolves to.
|
|
54
57
|
- **[M] Audit manifest version ({active_version})** — only useful when investigating the older version specifically.
|
|
55
|
-
- **[X] Abort** — halt without producing a report. Run `[EX] Export Skill` to reconcile the manifest before re-running audit-skill."
|
|
58
|
+
- **[X] Abort** — halt without producing a report (exit 6, `halt_reason: "user-cancelled"`). Run `[EX] Export Skill` to reconcile the manifest before re-running audit-skill."
|
|
56
59
|
|
|
57
|
-
Default is **[N]**. Headless mode auto-selects **[N]** with a loud log line: `"headless: manifest active_version ({active_version}) is older than symlink target ({symlink_target}); auditing symlink target. Run export-skill to reconcile."`
|
|
60
|
+
Default is **[N]**. Headless mode auto-selects **[N]** with a loud log line: `"headless: manifest active_version ({active_version}) is older than symlink target ({symlink_target}); auditing symlink target. Run export-skill to reconcile."` When the manifest and on-disk state disagree, the working tree is the more honest signal under automation.
|
|
58
61
|
|
|
59
62
|
- When the symlink target's provenance is **older** than (or equal to) the manifest's `last_exported`, the symlink predates the export — this is the normal post-export shape, no gate needed. Resolve to the manifest's `active_version`.
|
|
60
63
|
- When only one of the two versions has a provenance map, resolve to the version that has one (the other is inert — auditing it would degrade to text-diff). Log the choice.
|
|
@@ -70,13 +73,17 @@ Which skill would you like to audit? Please provide the skill name or path."
|
|
|
70
73
|
- If missing → "Skill not found at `{resolved_skill_package}`. Check the path and try again."
|
|
71
74
|
- If found → Continue
|
|
72
75
|
|
|
76
|
+
**Headless default** (when `{headless_mode}`): the interactive prompt and its "check the path and try again" re-prompt cannot be answered under automation, so §1 halts deterministically instead of looping. This is the origin site for the exit-2 / exit-3 rows the Exit Codes table attributes to step 1 §1 — emit the `SKF_AUDIT_RESULT_JSON` error envelope on **stderr** (shape per SKILL.md → Result Contract; `status: "error"`, `drift_score: null`, `report_path: null`, `next_workflow: null`, `audit_ref: null`) at each halt:
|
|
77
|
+
- **No `skill_name` supplied** (neither name nor path given): HALT with **exit 2**, `halt_reason: "input-missing"`, `skill_name: null`. Log: `"headless: no skill_name supplied; cannot resolve interactively. Re-run with skill_name set."`
|
|
78
|
+
- **`SKILL.md` missing at `{resolved_skill_package}`**: HALT with **exit 3**, `halt_reason: "skill-not-found"`, `skill_name: {skill_name}`. Log: `"headless: skill not found at {resolved_skill_package}; no interactive retry. Check the exported skill name/path."`
|
|
79
|
+
|
|
73
80
|
### 2. Load Forge Tier
|
|
74
81
|
|
|
75
82
|
Load `{sidecar_path}/forge-tier.yaml` to detect available tools.
|
|
76
83
|
|
|
77
84
|
**If file missing:**
|
|
78
85
|
- "Setup-forge has not been run. Cannot determine tool availability. Run `[SF] Setup Forge` first."
|
|
79
|
-
- HALT
|
|
86
|
+
- HALT with **exit 3**, `halt_reason: "forge-tier-missing"`. When `{headless_mode}`, emit the error envelope on **stderr** (shape per SKILL.md → Result Contract) and log: `"headless: forge-tier.yaml missing at {sidecar_path}; run setup-forge. Aborting."`
|
|
80
87
|
|
|
81
88
|
**If found:**
|
|
82
89
|
- Extract tier level: Quick / Forge / Forge+ / Deep
|
|
@@ -118,24 +125,50 @@ Search for provenance map at `{forge_data_folder}/{skill_name}/{active_version}/
|
|
|
118
125
|
- `{source_root}`, `{baseline_commit}`, `{baseline_ref}` — used by §5 Resolve Source Path and §5b Detect Upstream Drift.
|
|
119
126
|
- `{reexport_map}` — `{<internal>: <public>}` mapping consumed by `structural-diff.md` §1 to collapse public-API renames before diffing.
|
|
120
127
|
|
|
121
|
-
If the script exits non-zero, surface the stderr as a
|
|
128
|
+
If the script exits non-zero, surface the stderr as a hard halt — the map is structurally invalid and downstream steps cannot proceed.
|
|
122
129
|
|
|
123
130
|
**If missing at both paths:**
|
|
124
131
|
- "No provenance map found for `{skill_name}`. This skill may not have been created by create-skill."
|
|
125
132
|
- "**Degraded mode available:** I can perform text-based comparison without provenance data. Findings will have T1-low confidence."
|
|
126
133
|
- "**[D]egraded mode** — proceed with text-diff only"
|
|
127
134
|
- "**[X]** — abort audit"
|
|
128
|
-
- Wait for user selection. If D, set `degraded_mode: true` and `confidence_mode = "degraded — all findings T1-low"`, then skip the normalize call above (no map to normalize). If X, halt workflow.
|
|
135
|
+
- Wait for user selection. If D, set `degraded_mode: true` and `confidence_mode = "degraded — all findings T1-low"`, then skip the normalize call above (no map to normalize). If X, halt workflow (exit 6, `halt_reason: "user-cancelled"`).
|
|
136
|
+
|
|
137
|
+
**Headless default** (when `{headless_mode}`): consume the pre-supplied `degraded` input from the Invocation Contract. If `degraded=true`, auto-select **[D]** (set `degraded_mode: true`, `confidence_mode = "degraded — all findings T1-low"`, skip the normalize call) and log: `"headless: no provenance map for {skill_name}; proceeding in degraded mode (text-diff, T1-low) per pre-supplied degraded=true."` If `degraded` is unset or false, auto-select **[X] abort** (exit 6, `halt_reason: "user-cancelled"`) and log: `"headless: no provenance map for {skill_name} and degraded not pre-supplied; aborting. Re-run with degraded=true for text-diff."` Never silently emit a low-confidence report under automation without explicit opt-in — same stance as the [A]-abort default at §5b's dirty-worktree sub-gate.
|
|
129
138
|
|
|
130
139
|
### Stack Skill Detection
|
|
131
140
|
|
|
132
141
|
`{is_stack_skill}` and `{legacy_stack_provenance}` are already resolved by the normalize call in §4 — no additional walk needed. Apply the post-detection logic:
|
|
133
142
|
|
|
134
143
|
If `{is_stack_skill}` is true and `constituents` array is present (compose-mode stack):
|
|
135
|
-
|
|
136
|
-
-
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
|
|
145
|
+
Re-hashing each constituent's live `metadata.json` and comparing against the compile-time snapshot is deterministic work — and the model cannot compute a `sha256:{hexdigest}` natively (it must shell out on every run). Delegate the whole read/hash/compare pass to the shared helper, which hashes byte-symmetrically with how `constituents[].metadata_hash` was written at compose time (the `sha256:`-prefixed digest of the raw `metadata.json` bytes). This mirrors the two sibling drift checks — `structural-diff.md` §4b (`skf-compare-file-hashes.py`) and `step-doc-drift.md` §2 (`skf-detect-docs.py compare-hashes`).
|
|
146
|
+
|
|
147
|
+
**Resolve `{compareConstituentHashesHelper}`** from `{compareConstituentHashesProbeOrder}`; first existing path wins.
|
|
148
|
+
|
|
149
|
+
Run one deterministic comparison subprocess over the provenance map's `constituents[]`. Relative constituent `skill_path` values (e.g. `skills/{skill-dir}/`) are project-root-relative, so resolve them against `{project-root}`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
uv run {compareConstituentHashesHelper} compare-constituent-hashes {provenanceMap} --skills-root {project-root}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Parse the emitted JSON:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"drifted": [{"skill_name": "...", "skill_path": "...", "stored_hash": "sha256:...", "current_hash": "sha256:..."}],
|
|
160
|
+
"fresh": [{"skill_name": "..."}],
|
|
161
|
+
"missing": [{"skill_name": "...", "skill_path": "...", "stored_hash": "sha256:...", "reason": "metadata-not-found|incomplete-record"}],
|
|
162
|
+
"skipped_null_hash": [{"skill_name": "..."}],
|
|
163
|
+
"stats": {"total": N, "drifted": N, "fresh": N, "missing": N, "skipped_null_hash": N}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- **Flag every entry in `drifted[]` as constituent drift with severity HIGH** — its live `metadata.json` differs from the compile-time snapshot recorded in the provenance map.
|
|
168
|
+
- `fresh[]` are unchanged constituents. `missing[]` (each carrying a `reason`) are constituents whose `metadata.json` could not be located (`metadata-not-found`) or whose provenance record lacked `skill_name`/`skill_path` (`incomplete-record`) — surface these as a lower-severity note, not HIGH drift. `skipped_null_hash[]` had no compile-time baseline hash (recorded from a references/ cascade) — never reported as drift.
|
|
169
|
+
- Record the constituent freshness results (the four buckets + `stats`) for the report — read the counts straight from `stats`, no manual recount.
|
|
170
|
+
|
|
171
|
+
**If `uv`/the helper cannot execute** (e.g. claude.ai web): fall back to hashing by hand — for each constituent, read its `metadata.json` at `{constituent.skill_path}/active/{constituent.skill_name}/metadata.json` (resolve `skill_path` against `{project-root}` when relative, use as-is when absolute), SHA-256 the raw bytes, and compare against `constituent.metadata_hash` (a stored bare-hex form still matches after stripping any `sha256:` prefix from both sides). Flag mismatches as HIGH constituent drift.
|
|
139
172
|
|
|
140
173
|
If `{legacy_stack_provenance}` is true: log a note that this stack uses v1 provenance format with reduced audit depth (library-level only, no per-export verification).
|
|
141
174
|
|
|
@@ -150,7 +183,7 @@ If `{legacy_stack_provenance}` is true: log a note that this stack uses v1 prove
|
|
|
150
183
|
- Ask user: "Please provide the path to the current source code."
|
|
151
184
|
- `baseline_commit` and `baseline_ref` are unavailable — §5b will short-circuit
|
|
152
185
|
|
|
153
|
-
**Validate:** Confirm source directory exists and
|
|
186
|
+
**Validate:** Confirm the source directory exists and is accessible. If it is missing or unreadable → HALT with **exit 3**, `halt_reason: "source-dir-missing"`. When `{headless_mode}`, emit the error envelope on **stderr** (shape per SKILL.md → Result Contract) and log: `"headless: source directory {source_root} from the provenance map no longer exists; aborting."`
|
|
154
187
|
|
|
155
188
|
### 5b. Detect Upstream Drift
|
|
156
189
|
|
|
@@ -221,7 +254,10 @@ When skipping, log the reason, then set the audit-ref context variables to basel
|
|
|
221
254
|
- **[F]:** Run `git -C {source_root} checkout --force {chosen_ref}` instead of the plain checkout. Record `pre_checkout_force_discard: true` in workflow context for step 6 to surface as a loud warning. Skip the stash path.
|
|
222
255
|
- **Other input:** help user, redisplay the sub-gate.
|
|
223
256
|
|
|
224
|
-
**Headless default** (when `{headless_mode}`):
|
|
257
|
+
**Headless default** (when `{headless_mode}`): consume the pre-supplied `dirty_worktree_choice` from the Invocation Contract — the operator's explicit answer is the consent that a silent working-tree mutation would otherwise lack.
|
|
258
|
+
- **`dirty_worktree_choice=T`**: run the `[T]` transient-stash path. Log: `"headless: dirty worktree at {source_root}; stashing before checkout per pre-supplied dirty_worktree_choice=T."`
|
|
259
|
+
- **`dirty_worktree_choice=F` with `force=true`**: run the `[F]` force-checkout path — `force=true` is the required consent to discard uncommitted changes irrecoverably. Log: `"headless: dirty worktree at {source_root}; force-discarding uncommitted changes per pre-supplied dirty_worktree_choice=F force=true."`
|
|
260
|
+
- **`dirty_worktree_choice=A`, unset, or `=F` without `force=true`**: auto-select **[A] Abort** (exit 6, `halt_reason: "user-cancelled"`). Abort is the safe default, and a force-discard without `force=true` consent is refused rather than executed. Log: `"headless: dirty worktree detected at {source_root}; refusing to checkout {chosen_ref} (dirty_worktree_choice={value or 'unset'}). Pass dirty_worktree_choice=T, or =F with force=true, to proceed non-interactively."` Stashing that is never popped could lose work; force-checkout without consent could destroy uncommitted work outright — so both require an explicit pre-supplied choice.
|
|
225
261
|
|
|
226
262
|
If `git status --porcelain` is empty, skip the sub-gate and proceed directly to the checkout.
|
|
227
263
|
|
|
@@ -230,7 +266,10 @@ When skipping, log the reason, then set the audit-ref context variables to basel
|
|
|
230
266
|
- **[X]:** HALT workflow — do not create drift report.
|
|
231
267
|
- **Other input:** help user, redisplay gate.
|
|
232
268
|
|
|
233
|
-
**Headless default** (when `{headless_mode}`):
|
|
269
|
+
**Headless default** (when `{headless_mode}`): consume the pre-supplied `upstream_drift_choice` from the Invocation Contract.
|
|
270
|
+
- **`upstream_drift_choice=S`, or unset**: auto-select **[S] Stay-on-baseline** (default). Set `audit_ref = baseline_ref`, `audit_ref_source = "baseline"`, `audit_commit = baseline_commit`. Log: `"headless: upstream drift detected ({baseline_ref} → {latest_tag or remote_head}); staying on baseline per upstream_drift_choice={value or 'default S'}. Pass upstream_drift_choice=C to audit against latest."` Defaulting to a checkout would mutate the working tree without consent, so `[S]` remains the default when no choice is supplied.
|
|
271
|
+
- **`upstream_drift_choice=C`**: run the `[C] Checkout-and-audit-against-latest` path above — the operator's pre-supplied choice is the explicit consent that a silent ref change would otherwise lack. The dirty-worktree sub-gate still applies and consults its own pre-supplied `dirty_worktree_choice`. Log: `"headless: upstream drift detected; checking out {latest_tag or remote_head} per pre-supplied upstream_drift_choice=C."`
|
|
272
|
+
- **`upstream_drift_choice=X`**: HALT the workflow (exit 6, `halt_reason: "user-cancelled"`) — do not create a drift report. Log: `"headless: upstream drift detected; aborting per pre-supplied upstream_drift_choice=X."`
|
|
234
273
|
|
|
235
274
|
5. **Record for report:** store `audit_ref`, `audit_ref_source`, `audit_commit`, `latest_tag`, `remote_head`, and `baseline_commit` in context. Step-06 surfaces them in the Provenance section so readers can tell which comparison actually ran.
|
|
236
275
|
|
|
@@ -252,7 +291,7 @@ When skipping, log the reason, then set the audit-ref context variables to basel
|
|
|
252
291
|
- **[D]:** Read the prior report's findings_list (parse the Structural/Semantic/Severity sections, or the appended findings tables) and stash as `prior_findings` in workflow context. Run the new audit normally. In step 6 (report.md), after the Remediation Suggestions section, emit a `## Diff Against Prior Report` subsection summarizing added / removed / changed findings vs `prior_findings`.
|
|
253
292
|
- **[R]:** Load the prior report's frontmatter (`stepsCompleted`, `drift_score`, any intermediate state). Set `{outputFile}` to the prior report path (do NOT create a new one). Determine the next un-completed step from `stepsCompleted` and skip forward to it; downstream steps append to the existing report.
|
|
254
293
|
|
|
255
|
-
> **Note (resumability):** the
|
|
294
|
+
> **Note (resumability):** the template frontmatter captures `stepsCompleted` and `drift_score` but does not persist an intermediate findings_list between stages. If [R] is selected and stepsCompleted indicates the prior run halted after structural-diff or semantic-diff, the appended report-body sections (`## Structural Drift`, `## Semantic Drift`, `## Severity Classification`) serve as the implicit intermediate state — re-parse them on resume rather than re-running completed stages.
|
|
256
295
|
|
|
257
296
|
- **Other input:** help user, redisplay the gate.
|
|
258
297
|
|
|
@@ -268,7 +307,9 @@ Create `{outputFile}` from `{templateFile}`:
|
|
|
268
307
|
- Set `stepsCompleted: ['init']`
|
|
269
308
|
- Fill Audit Summary skeleton with loaded baseline data
|
|
270
309
|
|
|
271
|
-
|
|
310
|
+
If the write fails (read-only mount, disk full, permissions denied) → HALT with **exit 4**, `halt_reason: "write-failed"`. When `{headless_mode}`, emit the error envelope on **stderr** (shape per SKILL.md → Result Contract).
|
|
311
|
+
|
|
312
|
+
### 7. Present Baseline Summary and Confirm (User Gate)
|
|
272
313
|
|
|
273
314
|
"**Audit Baseline Loaded**
|
|
274
315
|
|
|
@@ -290,22 +331,7 @@ Create `{outputFile}` from `{templateFile}`:
|
|
|
290
331
|
|
|
291
332
|
**Ready to begin drift analysis?**"
|
|
292
333
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
Display: "**Select:** [C] Continue to Analysis"
|
|
296
|
-
|
|
297
|
-
#### Menu Handling Logic:
|
|
298
|
-
|
|
299
|
-
- IF C: Save baseline to {outputFile}, update frontmatter stepsCompleted, then load, read entire file, then execute {nextStepFile}
|
|
300
|
-
- IF Any other: help user, then [Redisplay Menu Options](#8-present-menu-options)
|
|
301
|
-
|
|
302
|
-
#### EXECUTION RULES:
|
|
303
|
-
|
|
304
|
-
- ALWAYS halt and wait for user input after presenting menu
|
|
305
|
-
- **GATE [default: C]** — If `{headless_mode}`: auto-proceed with [C] Continue, log: "headless: auto-continue past baseline confirmation"
|
|
306
|
-
- ONLY proceed to next step when user selects 'C'
|
|
307
|
-
|
|
308
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
334
|
+
Halt and wait for the user's go-ahead. Only proceed once the drift report has been created with baseline data populated. On confirmation, save the baseline to `{outputFile}`, append `'init'` to the frontmatter `stepsCompleted`, then load, read the entire file, and execute `{nextStepFile}`. On any other input, help the user, then re-ask.
|
|
309
335
|
|
|
310
|
-
|
|
336
|
+
**GATE [default: proceed]** — if `{headless_mode}`, auto-proceed and log: "headless: auto-continue past baseline confirmation".
|
|
311
337
|
|
|
@@ -14,7 +14,7 @@ Re-scan the source code using the current forge tier tools to build a fresh extr
|
|
|
14
14
|
## Rules
|
|
15
15
|
|
|
16
16
|
- Focus only on extracting current source state — do not compare yet (that's Step 03)
|
|
17
|
-
-
|
|
17
|
+
- Extract every file in the bounded scan list — a file skipped here makes step 3 flag its exports as false "removed" drift
|
|
18
18
|
- Use subprocess Pattern 2 (per-file deep analysis) when available for AST extraction; if unavailable, extract in main thread file by file
|
|
19
19
|
|
|
20
20
|
## MANDATORY SEQUENCE
|
|
@@ -53,13 +53,10 @@ Audit-skill detects drift on files that were in scope during create-skill. The a
|
|
|
53
53
|
|
|
54
54
|
**If a provenance map was loaded in step 1** (normal mode):
|
|
55
55
|
|
|
56
|
-
1.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
3. Verify each path under `{source_root}`. Files that existed at creation time but are now missing are **not** errors at this stage — keep them in the list so step 3 can classify them as DELETED. Handling missing files is step 3's job, not step 2's.
|
|
61
|
-
4. Record `bounded_scan: true` and `bounded_scan_source: "provenance-map"` in context for the evidence report.
|
|
62
|
-
5. Report:
|
|
56
|
+
1. The **bounded scan list** is `{bounded_scan_files}` — the union of `entries[].source_file` and `file_entries[].source_file`, deduplicated, sorted, and forward-slash normalized by init.md §4's `skf-load-provenance.py normalize` call. Consume it directly; do **not** re-walk the provenance map to rebuild it. The union/dedup/sort has one correct answer per map and is already scripted — re-deriving it in-prompt risks diverging from step 3, which diffs against the same normalized projection.
|
|
57
|
+
2. Verify each path under `{source_root}`. Files that existed at creation time but are now missing are **not** errors at this stage — keep them in the list so step 3 can classify them as DELETED. Handling missing files is step 3's job, not step 2's.
|
|
58
|
+
3. Record `bounded_scan: true` and `bounded_scan_source: "provenance-map"` in context for the evidence report.
|
|
59
|
+
4. Report:
|
|
63
60
|
|
|
64
61
|
"**Bounded scan:** {count} files from provenance map ({provenance_date})."
|
|
65
62
|
|
|
@@ -76,7 +73,7 @@ Audit-skill detects drift on files that were in scope during create-skill. The a
|
|
|
76
73
|
|
|
77
74
|
### 3. Extract Current Exports
|
|
78
75
|
|
|
79
|
-
**
|
|
76
|
+
**For each file in the bounded scan list from §2, launch a subprocess that:**
|
|
80
77
|
1. Loads the source file
|
|
81
78
|
2. Extracts all public exports using tier-appropriate method
|
|
82
79
|
3. Records: export name, type, signature, file path, line number, confidence tier
|
|
@@ -86,7 +83,7 @@ Audit-skill detects drift on files that were in scope during create-skill. The a
|
|
|
86
83
|
|
|
87
84
|
**If subprocess unavailable:** Perform extraction in main thread, processing each file sequentially.
|
|
88
85
|
|
|
89
|
-
**Build extraction snapshot
|
|
86
|
+
**Build extraction snapshot and persist it to `{forge_version}/extraction-snapshot.json`** — step 3 (`structural-diff.md`) reads this file directly, so it must be written to disk, not merely held in context:
|
|
90
87
|
```
|
|
91
88
|
{
|
|
92
89
|
"extraction_date": "{timestamp}",
|
|
@@ -108,15 +105,17 @@ Audit-skill detects drift on files that were in scope during create-skill. The a
|
|
|
108
105
|
}
|
|
109
106
|
```
|
|
110
107
|
|
|
108
|
+
Record the written path as `{extractionSnapshot}` in workflow context — step 3 passes it to the deterministic structural-diff helper.
|
|
109
|
+
|
|
111
110
|
### 4. Deep Tier Enhancement (Deep Only)
|
|
112
111
|
|
|
113
|
-
**
|
|
112
|
+
**If forge tier is Deep:**
|
|
114
113
|
|
|
115
114
|
Read the `qmd_collections` registry from `{sidecar_path}/forge-tier.yaml`.
|
|
116
115
|
|
|
117
116
|
Find the collection entry matching the current skill: look for an entry where `skill_name` matches the current skill being audited AND `type` is `"extraction"`.
|
|
118
117
|
|
|
119
|
-
Three collection states must be handled distinctly
|
|
118
|
+
Three collection states must be handled distinctly — semantic-diff.md §2 branches the same way:
|
|
120
119
|
|
|
121
120
|
**If a matching extraction collection is found and populated** (pre-query probe via `qmd ls {collection_name}` or equivalent returns one or more files):
|
|
122
121
|
Query qmd_bridge against the `{skill_name}-extraction` collection for temporal context on each extracted export:
|
|
@@ -135,12 +134,12 @@ Continue without T2 enrichment — the unpopulated collection is a setup gap, no
|
|
|
135
134
|
Log: "No QMD extraction collection found for {skill_name}. Temporal enrichment skipped. Re-run [CS] Create Skill to generate the collection."
|
|
136
135
|
Continue without T2 enrichment — this is not an error.
|
|
137
136
|
|
|
138
|
-
**
|
|
137
|
+
**If forge tier is Quick, Forge, or Forge+:**
|
|
139
138
|
Skip this section. Temporal context requires Deep tier.
|
|
140
139
|
|
|
141
140
|
### 4b. CCC Rename Detection (Forge+ and Deep with ccc)
|
|
142
141
|
|
|
143
|
-
**
|
|
142
|
+
**If `tools.ccc` is true in forge-tier.yaml:**
|
|
144
143
|
|
|
145
144
|
For each export in the skill baseline that was NOT found at its recorded file path during re-extraction (potential "deleted" export):
|
|
146
145
|
|
|
@@ -153,7 +152,7 @@ For each export in the skill baseline that was NOT found at its recorded file pa
|
|
|
153
152
|
|
|
154
153
|
CCC failures: skip rename detection silently, proceed with standard structural diff.
|
|
155
154
|
|
|
156
|
-
**
|
|
155
|
+
**If `tools.ccc` is false:** Skip this section silently.
|
|
157
156
|
|
|
158
157
|
### 5. Validate Extraction Completeness
|
|
159
158
|
|
|
@@ -174,23 +173,5 @@ CCC failures: skip rename detection silently, proceed with standard structural d
|
|
|
174
173
|
|
|
175
174
|
### 6. Update Report and Auto-Proceed
|
|
176
175
|
|
|
177
|
-
Update {outputFile} frontmatter
|
|
178
|
-
- Append `'re-index'` to `stepsCompleted`
|
|
179
|
-
|
|
180
|
-
### 7. Present MENU OPTIONS
|
|
181
|
-
|
|
182
|
-
Display: "**Proceeding to structural diff...**"
|
|
183
|
-
|
|
184
|
-
#### Menu Handling Logic:
|
|
185
|
-
|
|
186
|
-
- After extraction is complete and frontmatter updated, immediately load, read entire file, then execute {nextStepFile}
|
|
187
|
-
|
|
188
|
-
#### EXECUTION RULES:
|
|
189
|
-
|
|
190
|
-
- This is an auto-proceed analysis step with no user choices
|
|
191
|
-
- Proceed directly to next step after extraction
|
|
192
|
-
|
|
193
|
-
## CRITICAL STEP COMPLETION NOTE
|
|
194
|
-
|
|
195
|
-
ONLY WHEN the extraction snapshot is complete with all source files processed will you then load and read fully `{nextStepFile}` to execute and begin structural comparison.
|
|
176
|
+
Update {outputFile} frontmatter — append `'re-index'` to `stepsCompleted`. Once the extraction snapshot is complete with all source files processed, load, read fully, and execute `{nextStepFile}` (structural diff).
|
|
196
177
|
|
|
@@ -34,24 +34,7 @@ Update the ## Audit Summary section at the top of {outputFile} with final calcul
|
|
|
34
34
|
|
|
35
35
|
### 2. Generate Remediation Suggestions
|
|
36
36
|
|
|
37
|
-
For
|
|
38
|
-
|
|
39
|
-
**CRITICAL findings remediation:**
|
|
40
|
-
- Removed export → "Remove reference to `{export_name}` from SKILL.md section {section}. Export no longer exists at `{file}:{line}`."
|
|
41
|
-
- Changed signature → "Update `{export_name}` signature in SKILL.md from `{old_signature}` to `{new_signature}`. See `{file}:{line}`."
|
|
42
|
-
- Renamed export → "Replace `{old_name}` with `{new_name}` throughout SKILL.md. Renamed at `{file}:{line}`."
|
|
43
|
-
|
|
44
|
-
**HIGH findings remediation:**
|
|
45
|
-
- New public API (>3) → "Add documentation for {count} new exports to SKILL.md: {export_list}. Consider running update-skill workflow."
|
|
46
|
-
- Deprecated API → "Mark `{export_name}` as deprecated in SKILL.md. Current replacement: `{replacement}` at `{file}:{line}`."
|
|
47
|
-
|
|
48
|
-
**MEDIUM findings remediation:**
|
|
49
|
-
- Moved function → "Update file reference for `{export_name}` from `{old_file}` to `{new_file}:{line}`."
|
|
50
|
-
- New exports (1-3) → "Consider adding `{export_names}` to SKILL.md for completeness."
|
|
51
|
-
- Changed convention → "Review convention documentation in SKILL.md for currency."
|
|
52
|
-
|
|
53
|
-
**LOW findings remediation:**
|
|
54
|
-
- Style changes → "Optional: Update style references in SKILL.md to reflect current conventions."
|
|
37
|
+
For each classified drift finding, write one concrete remediation derived from the finding itself: **what** to change in the audited **SKILL.md** (or its `references/`) — not the source code — **where** (the section plus the source `{file}:{line}` the finding cites), and **why**. Set effort (`low`/`medium`/`high`) by how much of the skill doc the change touches. A reviewer should be able to act on each row without re-deriving the finding.
|
|
55
38
|
|
|
56
39
|
Append to {outputFile}:
|
|
57
40
|
|
|
@@ -125,50 +108,20 @@ Append to {outputFile}:
|
|
|
125
108
|
|
|
126
109
|
Update {outputFile} frontmatter:
|
|
127
110
|
- Append `'report'` to `stepsCompleted`
|
|
128
|
-
- Set `drift_score` to
|
|
111
|
+
- Set `drift_score` to the score from step 5's classification helper
|
|
129
112
|
- Set `nextWorkflow` to `'update-skill'` if CRITICAL or HIGH findings, otherwise leave empty
|
|
130
113
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"**Audit Complete: {skill_name}**
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
**Overall Drift Score: {CLEAN / MINOR / SIGNIFICANT / CRITICAL}**
|
|
138
|
-
|
|
139
|
-
| Severity | Count |
|
|
140
|
-
|----------|-------|
|
|
141
|
-
| CRITICAL | {count} |
|
|
142
|
-
| HIGH | {count} |
|
|
143
|
-
| MEDIUM | {count} |
|
|
144
|
-
| LOW | {count} |
|
|
145
|
-
| **Total** | {total} |
|
|
114
|
+
If finalizing the report or writing the result JSON below fails (read-only mount, disk full, permissions denied) → HALT with **exit 4**, `halt_reason: "write-failed"`. When `{headless_mode}`, emit the error envelope on **stderr** (shape per SKILL.md → Result Contract) with `report_path: null`.
|
|
146
115
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
{IF CRITICAL/HIGH findings:}
|
|
150
|
-
**Action Required:** {count} priority items need remediation. Recommend running `[US] Update Skill` workflow.
|
|
151
|
-
|
|
152
|
-
{IF MEDIUM/LOW only:}
|
|
153
|
-
**Minor Drift:** Skill is functional but could benefit from updates. See report for details.
|
|
154
|
-
|
|
155
|
-
{IF CLEAN:}
|
|
156
|
-
**All Clear:** No drift detected. Skill accurately reflects current source code.
|
|
157
|
-
|
|
158
|
-
---
|
|
116
|
+
### 5. Present Final Report Summary
|
|
159
117
|
|
|
160
|
-
**
|
|
161
|
-
{IF findings exist:}
|
|
162
|
-
1. **[US] Update Skill** — Automatically apply remediations from this drift report
|
|
163
|
-
2. **Review report** — Manual review at `{outputFile}`
|
|
118
|
+
Present a concise completion summary to the user conveying: the skill name, the **overall drift score** (CLEAN / MINOR / SIGNIFICANT / CRITICAL), the severity-count table (CRITICAL / HIGH / MEDIUM / LOW / Total), and the saved report path (`{outputFile}`). Close with the next-action recommendation matching the drift level:
|
|
164
119
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
---
|
|
120
|
+
- **CRITICAL or HIGH findings** → action required: recommend running the `[US] Update Skill` workflow to apply the priority remediations; manual review at `{outputFile}` is the alternative.
|
|
121
|
+
- **MEDIUM or LOW only** → minor drift: manual updates suffice, or run `[US] Update Skill` for automated remediation.
|
|
122
|
+
- **CLEAN** → no action needed; the skill is current and ready for `[EX] Export Skill`.
|
|
170
123
|
|
|
171
|
-
**
|
|
124
|
+
This summary reads as final but is **not** the terminal step — proceed to §6.
|
|
172
125
|
|
|
173
126
|
### Result Contract
|
|
174
127
|
|
|
@@ -182,7 +135,7 @@ SKF_AUDIT_RESULT_JSON: {"status":"success","skill_name":"{skill_name}","drift_sc
|
|
|
182
135
|
|
|
183
136
|
Field rules: `next_workflow` is `"update-skill"` when CRITICAL or HIGH findings exist (matches the frontmatter `nextWorkflow` set in §4), otherwise `null`. `audit_ref` carries the resolved value from step 1 §5b (`baseline_ref` when no upstream drift was detected, `latest_tag` or `remote_head` when the operator chose `[C] Checkout-and-audit-against-latest`).
|
|
184
137
|
|
|
185
|
-
**
|
|
138
|
+
**Hard-halt envelope (headless only).** Every hard halt emits this same envelope shape on **stderr** with `status: "error"` and the `exit_code` / `halt_reason` for its failure class (per SKILL.md → Exit Codes and Result Contract), produced at the halting site before exit — it is the only failure signal a wrapping pipeline receives, so log it before exiting. `drift_score` carries its last known value (`null` if classification never ran); `report_path` is `null` when the report write failed.
|
|
186
139
|
|
|
187
140
|
**Post-audit hook (optional).** If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it as:
|
|
188
141
|
|
|
@@ -194,5 +147,5 @@ where `{result_json_path}` is the per-run record path written above (`{forge_ver
|
|
|
194
147
|
|
|
195
148
|
### 6. Chain to Health Check
|
|
196
149
|
|
|
197
|
-
|
|
150
|
+
Only when the report has been written, presented, and the result contract saved do you then load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the user-facing summary reads as final.
|
|
198
151
|
|