bmad-module-skill-forge 2.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +49 -49
- package/docs/404.md +10 -8
- package/docs/_data/pinned.yaml +22 -13
- package/docs/_internal/STABILITY.md +58 -35
- package/docs/agents.md +39 -53
- package/docs/architecture.md +65 -51
- package/docs/bmad-synergy.md +53 -51
- package/docs/campaign.md +127 -75
- package/docs/concepts.md +34 -28
- package/docs/examples.md +70 -68
- package/docs/forge-auto.md +34 -31
- package/docs/getting-started.md +118 -57
- package/docs/how-it-works.md +25 -21
- package/docs/index.md +15 -15
- package/docs/skill-model.md +70 -59
- package/docs/troubleshooting.md +143 -18
- package/docs/verifying-a-skill.md +87 -57
- package/docs/why-skf.md +24 -22
- package/docs/workflows.md +274 -110
- package/package.json +18 -10
- package/src/forger/preferences.yaml +8 -1
- package/src/knowledge/agentskills-spec.md +2 -0
- package/src/knowledge/ccc-bridge.md +40 -24
- package/src/knowledge/confidence-tiers.md +9 -5
- package/src/knowledge/overview.md +3 -3
- package/src/knowledge/progressive-capability.md +8 -8
- package/src/knowledge/provenance-tracking.md +9 -5
- package/src/knowledge/qmd-registry.md +3 -3
- package/src/knowledge/skf-knowledge-index.csv +2 -2
- package/src/knowledge/skill-lifecycle.md +18 -11
- package/src/knowledge/split-body-strategy.md +0 -3
- package/src/knowledge/tool-resolution.md +4 -2
- package/src/knowledge/version-paths.md +101 -15
- package/src/module-help.csv +3 -3
- package/src/module.yaml +10 -0
- package/src/shared/data/ast-grep-recipes.yaml +608 -0
- package/src/shared/data/ide-context-files.json +30 -0
- package/src/shared/health-check.md +31 -59
- package/src/shared/references/description-guard-protocol.md +12 -9
- package/src/shared/references/feasibility-report-schema.md +19 -13
- package/src/shared/references/headless-gate-convention.md +55 -3
- package/src/shared/references/output-contract-schema.md +19 -8
- package/src/shared/references/pipeline-contracts.md +94 -29
- package/src/shared/references/tessl-review.md +77 -0
- package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
- package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
- package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
- package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
- package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
- package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
- package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
- package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
- package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
- package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
- package/src/shared/scripts/skf-atomic-write.py +233 -64
- package/src/shared/scripts/skf-build-change-manifest.py +2753 -28
- package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
- package/src/shared/scripts/skf-check-unit-records.py +184 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
- package/src/shared/scripts/skf-classify-changed-files.py +772 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +267 -105
- package/src/shared/scripts/skf-count-imports.py +1867 -0
- package/src/shared/scripts/skf-count-tokens.py +1 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +21 -3
- package/src/shared/scripts/skf-description-guard.py +99 -11
- package/src/shared/scripts/skf-detect-docs.py +694 -2
- package/src/shared/scripts/skf-detect-language.py +486 -107
- package/src/shared/scripts/skf-detect-registry.py +887 -0
- package/src/shared/scripts/skf-detect-scripts-assets.py +197 -55
- package/src/shared/scripts/skf-detect-tools.py +209 -27
- package/src/shared/scripts/skf-detect-workspaces.py +506 -30
- package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
- package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
- package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
- package/src/shared/scripts/skf-extract-public-api.py +5247 -96
- package/src/shared/scripts/skf-extraction-inventory.py +1280 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +664 -0
- package/src/shared/scripts/skf-fetch-temporal.py +674 -0
- package/src/shared/scripts/skf-find-cycles.py +97 -29
- package/src/shared/scripts/skf-find-test-report.py +476 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +469 -205
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +700 -0
- package/src/shared/scripts/skf-hash-content.py +180 -20
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +161 -27
- package/src/shared/scripts/skf-manifest-ops.py +1 -1
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
- package/src/shared/scripts/skf-merge-doc-urls.py +143 -20
- package/src/shared/scripts/skf-names-present.py +221 -0
- package/src/shared/scripts/skf-pair-intersect.py +119 -28
- package/src/shared/scripts/skf-parse-gaps.py +1384 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-preflight.py +176 -28
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +64 -8
- package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
- package/src/shared/scripts/skf-quick-batch.py +774 -0
- package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
- package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
- package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
- package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
- package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
- package/src/shared/scripts/skf-resolve-authoritative-files.py +155 -14
- package/src/shared/scripts/skf-resolve-package.py +523 -106
- package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
- package/src/shared/scripts/skf-run-lock.py +577 -0
- package/src/shared/scripts/skf-scan-manifests.py +998 -57
- package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
- package/src/shared/scripts/skf-severity-classify.py +712 -108
- package/src/shared/scripts/skf-shape-detect.py +298 -204
- package/src/shared/scripts/skf-shard-body.py +9 -8
- package/src/shared/scripts/skf-skill-inventory.py +2412 -89
- package/src/shared/scripts/skf-skills-module.py +472 -0
- package/src/shared/scripts/skf-source-tree.py +1796 -0
- package/src/shared/scripts/skf-structural-diff.py +787 -121
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +120 -67
- package/src/shared/scripts/skf-update-run-state.py +624 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
- package/src/shared/scripts/skf-validate-brief-schema.py +23 -2
- package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
- package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
- package/src/shared/scripts/skf-validate-output.py +712 -64
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +116 -10
- package/src/shared/scripts/skf-verify-provenance-completeness.py +3945 -152
- package/src/shared/scripts/skf-write-skill-brief.py +406 -25
- package/src/shared/tool-requirements.yaml +201 -0
- package/src/skf-analyze-source/SKILL.md +41 -36
- package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
- package/src/skf-analyze-source/customize.toml +36 -24
- package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
- package/src/skf-analyze-source/references/continue.md +2 -13
- package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
- package/src/skf-analyze-source/references/generate-briefs.md +121 -55
- package/src/skf-analyze-source/references/headless-contract.md +42 -15
- package/src/skf-analyze-source/references/identify-units.md +96 -110
- package/src/skf-analyze-source/references/init.md +73 -71
- package/src/skf-analyze-source/references/map-and-detect.md +123 -105
- package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
- package/src/skf-analyze-source/references/recommend.md +51 -70
- package/src/skf-analyze-source/references/scan-project.md +60 -84
- package/src/skf-analyze-source/references/scan-root.md +39 -0
- package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
- package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
- package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
- package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
- package/src/skf-audit-skill/SKILL.md +43 -51
- package/src/skf-audit-skill/assets/drift-report-template.md +28 -3
- package/src/skf-audit-skill/customize.toml +41 -20
- package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
- package/src/skf-audit-skill/references/doc-drift.md +141 -0
- package/src/skf-audit-skill/references/headless-contract.md +29 -0
- package/src/skf-audit-skill/references/health-check.md +2 -0
- package/src/skf-audit-skill/references/init.md +149 -210
- package/src/skf-audit-skill/references/re-index.md +103 -110
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +34 -25
- package/src/skf-audit-skill/references/severity-classify.md +79 -65
- package/src/skf-audit-skill/references/severity-rules.md +15 -6
- package/src/skf-audit-skill/references/structural-diff.md +80 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +712 -0
- package/src/skf-brief-skill/SKILL.md +37 -26
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +18 -25
- package/src/skf-brief-skill/assets/skill-brief-schema.md +30 -23
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +144 -127
- package/src/skf-brief-skill/references/confirm-brief.md +11 -9
- package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
- package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
- package/src/skf-brief-skill/references/gather-intent.md +87 -182
- package/src/skf-brief-skill/references/headless-args.md +84 -16
- package/src/skf-brief-skill/references/health-check.md +1 -1
- package/src/skf-brief-skill/references/invocation-contract.md +18 -13
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
- package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
- package/src/skf-brief-skill/references/scope-definition.md +123 -47
- package/src/skf-brief-skill/references/step-auto-brief.md +26 -100
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +7 -10
- package/src/skf-brief-skill/references/write-brief.md +66 -57
- package/src/skf-campaign/SKILL.md +31 -34
- package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
- package/src/skf-campaign/customize.toml +47 -16
- package/src/skf-campaign/references/campaign-contracts.md +39 -29
- package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
- package/src/skf-campaign/references/health-check.md +7 -16
- package/src/skf-campaign/references/step-01-setup.md +46 -84
- package/src/skf-campaign/references/step-02-strategy.md +31 -30
- package/src/skf-campaign/references/step-03-pins.md +26 -18
- package/src/skf-campaign/references/step-04-provenance.md +24 -23
- package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
- package/src/skf-campaign/references/step-06-batch.md +41 -34
- package/src/skf-campaign/references/step-07-capstone.md +40 -25
- package/src/skf-campaign/references/step-08-verify.md +28 -29
- package/src/skf-campaign/references/step-09-refine.md +29 -30
- package/src/skf-campaign/references/step-10-export.md +48 -36
- package/src/skf-campaign/references/step-11-maintenance.md +31 -34
- package/src/skf-campaign/references/step-resume.md +37 -68
- package/src/skf-campaign/scripts/campaign-deps.py +57 -18
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
- package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
- package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
- package/src/skf-campaign/scripts/campaign-report.py +204 -37
- package/src/skf-campaign/scripts/campaign-state.py +1128 -0
- package/src/skf-campaign/scripts/campaign-status.py +10 -74
- package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
- package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
- package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
- package/src/skf-campaign/templates/campaign-report-template.md +5 -1
- package/src/skf-campaign/templates/kickoff-template.md +0 -14
- package/src/skf-create-skill/SKILL.md +27 -20
- package/src/skf-create-skill/assets/compile-assembly-rules.md +11 -8
- package/src/skf-create-skill/assets/skill-sections.md +21 -11
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +36 -58
- package/src/skf-create-skill/references/batch-mode.md +81 -0
- package/src/skf-create-skill/references/ccc-index-check.md +27 -0
- package/src/skf-create-skill/references/compile.md +119 -70
- package/src/skf-create-skill/references/component-extraction.md +113 -91
- package/src/skf-create-skill/references/enrich.md +41 -23
- package/src/skf-create-skill/references/entry-points-by-hand.md +37 -0
- package/src/skf-create-skill/references/extract.md +204 -162
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +11 -11
- package/src/skf-create-skill/references/extraction-patterns.md +588 -201
- package/src/skf-create-skill/references/generate-artifacts.md +145 -93
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +53 -36
- package/src/skf-create-skill/references/relabel-rule.md +12 -0
- package/src/skf-create-skill/references/report.md +37 -57
- package/src/skf-create-skill/references/source-resolution-protocols.md +58 -169
- package/src/skf-create-skill/references/step-auto-shard.md +20 -93
- package/src/skf-create-skill/references/step-doc-rot.md +44 -60
- package/src/skf-create-skill/references/step-doc-sources.md +28 -19
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -59
- package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
- package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
- package/src/skf-create-skill/references/validate.md +173 -99
- package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
- package/src/skf-create-stack-skill/SKILL.md +29 -56
- package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
- package/src/skf-create-stack-skill/customize.toml +23 -12
- package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
- package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
- package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
- package/src/skf-create-stack-skill/references/generate-output.md +138 -128
- package/src/skf-create-stack-skill/references/init.md +90 -32
- package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
- package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
- package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
- package/src/skf-create-stack-skill/references/report.md +39 -33
- package/src/skf-create-stack-skill/references/validate.md +20 -39
- package/src/skf-drop-skill/SKILL.md +28 -51
- package/src/skf-drop-skill/customize.toml +44 -30
- package/src/skf-drop-skill/references/execute.md +83 -146
- package/src/skf-drop-skill/references/health-check.md +2 -0
- package/src/skf-drop-skill/references/invocation-contract.md +64 -0
- package/src/skf-drop-skill/references/report.md +46 -30
- package/src/skf-drop-skill/references/select.md +122 -124
- package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
- package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
- package/src/skf-export-skill/SKILL.md +40 -62
- package/src/skf-export-skill/assets/managed-section-format.md +14 -50
- package/src/skf-export-skill/assets/snippet-format.md +3 -1
- package/src/skf-export-skill/customize.toml +42 -13
- package/src/skf-export-skill/references/generate-snippet.md +54 -46
- package/src/skf-export-skill/references/health-check.md +2 -0
- package/src/skf-export-skill/references/invocation-contract.md +15 -0
- package/src/skf-export-skill/references/load-skill.md +88 -62
- package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
- package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
- package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
- package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
- package/src/skf-export-skill/references/package.md +15 -73
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
- package/src/skf-export-skill/references/result-envelope.md +31 -16
- package/src/skf-export-skill/references/summary.md +41 -17
- package/src/skf-export-skill/references/token-report.md +4 -12
- package/src/skf-export-skill/references/update-context.md +118 -237
- package/src/skf-forger/SKILL.md +61 -37
- package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
- package/src/skf-forger/customize.toml +43 -0
- package/src/skf-forger/references/pipeline-mode.md +73 -18
- package/src/skf-forger/scripts/forge-status.py +299 -0
- package/src/skf-forger/scripts/parse-pipeline.py +322 -54
- package/src/skf-forger/scripts/pipeline-gate.py +428 -0
- package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
- package/src/skf-quick-skill/SKILL.md +54 -53
- package/src/skf-quick-skill/assets/skill-template.md +4 -2
- package/src/skf-quick-skill/customize.toml +51 -20
- package/src/skf-quick-skill/references/batch-mode.md +74 -21
- package/src/skf-quick-skill/references/compile.md +30 -31
- package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
- package/src/skf-quick-skill/references/finalize.md +69 -18
- package/src/skf-quick-skill/references/halt-contract.md +41 -29
- package/src/skf-quick-skill/references/health-check.md +5 -3
- package/src/skf-quick-skill/references/quick-extract.md +114 -45
- package/src/skf-quick-skill/references/registry-resolution.md +9 -64
- package/src/skf-quick-skill/references/resolve-target.md +116 -66
- package/src/skf-quick-skill/references/write-and-validate.md +79 -40
- package/src/skf-refine-architecture/SKILL.md +43 -53
- package/src/skf-refine-architecture/customize.toml +49 -20
- package/src/skf-refine-architecture/references/compile.md +97 -54
- package/src/skf-refine-architecture/references/exit-codes.md +30 -0
- package/src/skf-refine-architecture/references/finding-storage.md +21 -0
- package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
- package/src/skf-refine-architecture/references/improvements.md +14 -20
- package/src/skf-refine-architecture/references/init.md +98 -57
- package/src/skf-refine-architecture/references/issue-detection.md +32 -39
- package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
- package/src/skf-refine-architecture/references/report.md +65 -54
- package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
- package/src/skf-rename-skill/SKILL.md +19 -54
- package/src/skf-rename-skill/customize.toml +32 -26
- package/src/skf-rename-skill/references/execute.md +230 -184
- package/src/skf-rename-skill/references/exit-codes.md +7 -7
- package/src/skf-rename-skill/references/health-check.md +1 -7
- package/src/skf-rename-skill/references/invocation-contract.md +25 -0
- package/src/skf-rename-skill/references/report.md +63 -52
- package/src/skf-rename-skill/references/select.md +197 -115
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
- package/src/skf-setup/SKILL.md +26 -26
- package/src/skf-setup/customize.toml +14 -10
- package/src/skf-setup/references/auto-index.md +50 -63
- package/src/skf-setup/references/ccc-index.md +29 -90
- package/src/skf-setup/references/detect-and-tier.md +58 -67
- package/src/skf-setup/references/health-check.md +2 -0
- package/src/skf-setup/references/invocation-contract.md +14 -0
- package/src/skf-setup/references/report.md +43 -166
- package/src/skf-setup/references/tier-rules.md +5 -5
- package/src/skf-setup/references/write-config.md +34 -57
- package/src/skf-test-skill/SKILL.md +17 -57
- package/src/skf-test-skill/assets/output-section-formats.md +33 -34
- package/src/skf-test-skill/customize.toml +27 -13
- package/src/skf-test-skill/references/coherence-check.md +134 -62
- package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
- package/src/skf-test-skill/references/coverage-check.md +209 -264
- package/src/skf-test-skill/references/detect-mode.md +5 -5
- package/src/skf-test-skill/references/external-validators.md +94 -82
- package/src/skf-test-skill/references/health-check.md +6 -7
- package/src/skf-test-skill/references/init.md +164 -91
- package/src/skf-test-skill/references/invocation-contract.md +66 -0
- package/src/skf-test-skill/references/migration-section-rules.md +5 -2
- package/src/skf-test-skill/references/report.md +164 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +42 -155
- package/src/skf-test-skill/references/source-access-protocol.md +18 -94
- package/src/skf-test-skill/references/step-hard-gate.md +51 -33
- package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
- package/src/skf-test-skill/scripts/build-result-context.py +380 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
- package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
- package/src/skf-test-skill/scripts/compute-score.py +223 -45
- package/src/skf-test-skill/scripts/gap-ledger.py +1666 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +1198 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +547 -79
- package/src/skf-test-skill/scripts/score-signatures.py +479 -0
- package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +133 -56
- package/src/skf-test-skill/templates/test-report-template.md +21 -15
- package/src/skf-update-skill/SKILL.md +38 -25
- package/src/skf-update-skill/customize.toml +27 -12
- package/src/skf-update-skill/references/detect-changes.md +253 -223
- package/src/skf-update-skill/references/gap-driven.md +336 -0
- package/src/skf-update-skill/references/health-check.md +31 -5
- package/src/skf-update-skill/references/init.md +254 -84
- package/src/skf-update-skill/references/invocation-contract.md +12 -0
- package/src/skf-update-skill/references/manual-section-rules.md +6 -23
- package/src/skf-update-skill/references/merge.md +182 -49
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +102 -35
- package/src/skf-update-skill/references/write.md +357 -155
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +183 -46
- package/src/skf-verify-stack/SKILL.md +23 -42
- package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
- package/src/skf-verify-stack/customize.toml +55 -22
- package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
- package/src/skf-verify-stack/references/coverage.md +67 -64
- package/src/skf-verify-stack/references/exit-codes.md +21 -8
- package/src/skf-verify-stack/references/init.md +107 -62
- package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
- package/src/skf-verify-stack/references/integrations.md +106 -65
- package/src/skf-verify-stack/references/report.md +59 -42
- package/src/skf-verify-stack/references/requirements.md +32 -9
- package/src/skf-verify-stack/references/synthesize.md +53 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
- package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
- package/tools/cli/commands/install.js +11 -4
- package/tools/cli/commands/status.js +15 -3
- package/tools/cli/commands/update.js +11 -4
- package/tools/cli/lib/tool-check.js +320 -0
- package/tools/cli/lib/version-check.js +22 -9
- package/tools/cli/skf-cli.js +4 -1
- package/.gitattributes +0 -16
- package/.nvmrc +0 -1
- package/docs/_internal/RELEASING.md +0 -679
- package/release-audits/v1.0.0-launch-audit.md +0 -728
- package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
- package/src/skf-campaign/manifest.yaml +0 -15
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
- package/src/skf-create-skill/references/ecosystem-check.md +0 -74
- package/src/skf-drop-skill/references/headless-contract.md +0 -18
- package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
- package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
- package/src/skf-update-skill/references/validate.md +0 -124
- package/tools/validate-docs-drift.js +0 -233
- package/tools/validate-docs-links.js +0 -665
|
@@ -1,46 +1,80 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# /// script
|
|
3
|
-
# requires-python = ">=3.
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# dependencies = ["pyyaml"]
|
|
4
5
|
# ///
|
|
5
6
|
"""Deterministic NEW_FILE detection for skf-update-skill (detect-changes Category D).
|
|
6
7
|
|
|
7
8
|
`skf-hash-content compare` classifies files already tracked in the provenance
|
|
8
9
|
map (UNCHANGED / MODIFIED_FILE / DELETED_FILE) but by design cannot report a
|
|
9
|
-
file that is present in source yet absent from the provenance map
|
|
10
|
+
file that is present in source yet absent from the provenance map: a NEW_FILE.
|
|
10
11
|
Deriving that set is a set-difference plus a filter with exactly one correct
|
|
11
12
|
answer per input: take every `source_file` in the scripts/assets inventory
|
|
12
13
|
(emitted by `skf-detect-scripts-assets detect`), subtract the paths already in
|
|
13
|
-
`file_entries[].source_file`, and set aside any user-authored `[MANUAL]` path
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
`file_entries[].source_file`, and set aside any user-authored `[MANUAL]` path,
|
|
15
|
+
any path the map's `entries[]` cite (code Category A and B track) and, with
|
|
16
|
+
--brief, any path the skill brief leaves out. Doing that subtraction in the
|
|
17
|
+
prompt drifts across runs; doing it here is byte-stable.
|
|
16
18
|
|
|
17
19
|
Usage:
|
|
18
|
-
skf-detect-scripts-assets.py detect <source-root>
|
|
19
|
-
| skf-new-file-diff.py <provenance-map-path>
|
|
20
|
+
skf-detect-scripts-assets.py detect <source-root> \\
|
|
21
|
+
| skf-new-file-diff.py <provenance-map-path> [--brief <brief-path>]
|
|
20
22
|
|
|
21
23
|
Reads the detect JSON on stdin (needs `scripts_inventory[]` and
|
|
22
24
|
`assets_inventory[]`, each row carrying `source_file`). Reads the provenance
|
|
23
|
-
map at the path argument (canonical `{file_entries: [...]
|
|
24
|
-
array of entries).
|
|
25
|
+
map at the path argument (canonical `{file_entries: [...], entries: [...]}`
|
|
26
|
+
object, or a bare array of file entries). A map with no `file_entries`, or a
|
|
27
|
+
null one, tracks no file (a skill with no script, asset or promoted document,
|
|
28
|
+
or a map written before the field existed); a `file_entries` or `entries`
|
|
29
|
+
that is neither null nor an array is bad input.
|
|
30
|
+
|
|
31
|
+
--brief <brief-path> (the skill's skill-brief.yaml) keeps a detected path
|
|
32
|
+
only when the brief takes it, by update-skill Category A's in-scope test
|
|
33
|
+
(skf-classify-changed-files.py load_scope, loaded from the shared
|
|
34
|
+
scripts folder, the one brief-scope test the script and asset detector's
|
|
35
|
+
and audit-skill's --brief use too, with the glob rules of
|
|
36
|
+
skf-resolve-authoritative-files.py): each `scope.include` and
|
|
37
|
+
`scope.exclude` pattern normalized (forward slashes, no leading `./`),
|
|
38
|
+
and a path in scope when an include
|
|
39
|
+
matches it and no exclude does. With an empty `scope.include`, a path is in
|
|
40
|
+
scope when no exclude matches it and its extension is one a file the map's
|
|
41
|
+
`entries[]` cite has, or one the brief's `language` uses. The brief's
|
|
42
|
+
`scripts_intent` and `assets_intent` are honoured too: `none` drops every
|
|
43
|
+
detected path of that kind, and an absent or free-text intent detects.
|
|
44
|
+
An empty --brief (an unbound brief path) is a usage error, never an
|
|
45
|
+
unscoped run. Every path, the map's and the detector's, is compared
|
|
46
|
+
stripped, with forward slashes and no leading `./`.
|
|
25
47
|
|
|
26
48
|
Output JSON (stdout):
|
|
27
49
|
{
|
|
28
50
|
"new_files": [ {"source_file": "...", "kind": "script|asset"}, ... ],
|
|
29
51
|
"skipped_manual": [ "...", ... ], # under scripts/[MANUAL]/ or assets/[MANUAL]/
|
|
30
52
|
"already_tracked": [ "...", ... ], # present in file_entries
|
|
53
|
+
"tracked_code": [ "...", ... ], # cited by entries[]: code, not a new file
|
|
54
|
+
"out_of_scope": [ "...", ... ], # --brief: outside the brief's scope
|
|
55
|
+
"intent_none": [ "...", ... ], # --brief: a kind whose intent is none
|
|
31
56
|
"stats": {"inventory_total": N, "new": N,
|
|
32
57
|
"skipped_manual": N, "already_tracked": N}
|
|
33
58
|
}
|
|
34
59
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
Each detected path lands in exactly one list, tested in this order:
|
|
61
|
+
already_tracked, skipped_manual, tracked_code, out_of_scope, intent_none,
|
|
62
|
+
and new_files for the rest. All arrays are sorted by
|
|
63
|
+
source_file; new_files carries the kind of the inventory it came from
|
|
64
|
+
(scripts -> "script", assets -> "asset"). A path found in both inventories is
|
|
65
|
+
counted once, resolved as "script". The three set-aside lists (tracked_code,
|
|
66
|
+
out_of_scope, intent_none) are never counted in `stats`, whose
|
|
67
|
+
inventory_total still counts every detected path.
|
|
38
68
|
|
|
39
69
|
Exit codes:
|
|
40
|
-
0 success
|
|
41
|
-
2 bad input (unreadable/invalid stdin JSON, missing/invalid
|
|
70
|
+
0 success (and --help)
|
|
71
|
+
2 bad input (a usage error, unreadable/invalid stdin JSON, missing/invalid
|
|
72
|
+
provenance map, a missing or unreadable brief, or a shared helper that
|
|
73
|
+
cannot be loaded)
|
|
42
74
|
"""
|
|
43
75
|
|
|
76
|
+
import argparse
|
|
77
|
+
import importlib.util
|
|
44
78
|
import json
|
|
45
79
|
import re
|
|
46
80
|
import sys
|
|
@@ -49,6 +83,13 @@ from pathlib import Path
|
|
|
49
83
|
# scripts/[MANUAL]/... or assets/[MANUAL]/... anywhere in the (posix) path
|
|
50
84
|
_MANUAL_RE = re.compile(r"(?:^|/)(?:scripts|assets)/\[MANUAL\]/")
|
|
51
85
|
|
|
86
|
+
# The shared helper that holds the scope test, installed beside this skill's
|
|
87
|
+
# folder: it loads skf-resolve-authoritative-files.py beside it.
|
|
88
|
+
SHARED_SCRIPTS = Path(__file__).resolve().parent.parent.parent / "shared" / "scripts"
|
|
89
|
+
CLASSIFIER = "skf-classify-changed-files.py"
|
|
90
|
+
|
|
91
|
+
_SHARED: dict[str, object] = {}
|
|
92
|
+
|
|
52
93
|
|
|
53
94
|
def _fail(msg: str) -> "NoReturn": # type: ignore[valid-type]
|
|
54
95
|
print(json.dumps({"error": msg}), file=sys.stderr)
|
|
@@ -56,37 +97,88 @@ def _fail(msg: str) -> "NoReturn": # type: ignore[valid-type]
|
|
|
56
97
|
|
|
57
98
|
|
|
58
99
|
def _posix(p: str) -> str:
|
|
59
|
-
|
|
100
|
+
"""A path as skf-resolve-authoritative-files.py's normalize_rel_path
|
|
101
|
+
writes it: stripped, forward slashes, no leading `./`."""
|
|
102
|
+
p = p.strip().replace("\\", "/")
|
|
103
|
+
while p.startswith("./"):
|
|
104
|
+
p = p[2:]
|
|
105
|
+
return p
|
|
60
106
|
|
|
61
107
|
|
|
62
|
-
def
|
|
63
|
-
"""
|
|
108
|
+
def _shared(filename: str):
|
|
109
|
+
"""A script of SHARED_SCRIPTS, loaded once; a failure to load it is bad input (exit 2)."""
|
|
110
|
+
module = _SHARED.get(filename)
|
|
111
|
+
if module is None:
|
|
112
|
+
path = SHARED_SCRIPTS / filename
|
|
113
|
+
try:
|
|
114
|
+
spec = importlib.util.spec_from_file_location("skf_" + filename[:-3].replace("-", "_"), path)
|
|
115
|
+
if spec is None or spec.loader is None or not path.is_file():
|
|
116
|
+
raise ImportError(f"{filename} not found in {SHARED_SCRIPTS}")
|
|
117
|
+
module = importlib.util.module_from_spec(spec)
|
|
118
|
+
spec.loader.exec_module(module)
|
|
119
|
+
except Exception as exc: # whatever the script raises, the run reports it
|
|
120
|
+
_fail(f"cannot load {filename} from {SHARED_SCRIPTS}: {exc}; re-install SKF")
|
|
121
|
+
_SHARED[filename] = module
|
|
122
|
+
return module
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _source_files(rows: list) -> set[str]:
|
|
126
|
+
found: set[str] = set()
|
|
127
|
+
for entry in rows:
|
|
128
|
+
if isinstance(entry, dict):
|
|
129
|
+
sf = entry.get("source_file")
|
|
130
|
+
if isinstance(sf, str) and _posix(sf):
|
|
131
|
+
found.add(_posix(sf))
|
|
132
|
+
return found
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def load_map_paths(provenance_path: Path) -> tuple[set[str], set[str]]:
|
|
136
|
+
"""(file_entries[].source_file, entries[].source_file) of the provenance map.
|
|
137
|
+
|
|
138
|
+
A missing or null `file_entries` (or `entries`) is an empty list; one
|
|
139
|
+
that is neither null nor an array is bad input. A bare array is the
|
|
140
|
+
file entries themselves.
|
|
141
|
+
"""
|
|
64
142
|
try:
|
|
65
143
|
data = json.loads(provenance_path.read_text(encoding="utf-8"))
|
|
66
144
|
except (json.JSONDecodeError, OSError) as exc:
|
|
67
145
|
_fail(f"failed to read provenance map {provenance_path}: {exc}")
|
|
68
146
|
|
|
69
147
|
if isinstance(data, list):
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
entries = data.get("file_entries")
|
|
73
|
-
if entries is None:
|
|
74
|
-
_fail(f"provenance map {provenance_path} has no `file_entries` field")
|
|
75
|
-
if not isinstance(entries, list):
|
|
76
|
-
_fail(f"`file_entries` in {provenance_path} is not an array")
|
|
77
|
-
else:
|
|
148
|
+
return _source_files(data), set()
|
|
149
|
+
if not isinstance(data, dict):
|
|
78
150
|
_fail(
|
|
79
151
|
f"provenance map {provenance_path} must be an object or array; "
|
|
80
152
|
f"got {type(data).__name__}"
|
|
81
153
|
)
|
|
154
|
+
rows = {}
|
|
155
|
+
for key in ("file_entries", "entries"):
|
|
156
|
+
value = data.get(key)
|
|
157
|
+
if value is None:
|
|
158
|
+
value = [] # a map that tracks no such row
|
|
159
|
+
if not isinstance(value, list):
|
|
160
|
+
_fail(f"`{key}` in {provenance_path} is not an array")
|
|
161
|
+
rows[key] = value
|
|
162
|
+
return _source_files(rows["file_entries"]), _source_files(rows["entries"])
|
|
82
163
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
164
|
+
|
|
165
|
+
def load_tracked_source_files(provenance_path: Path) -> set[str]:
|
|
166
|
+
"""Return the set of file_entries[].source_file already in the provenance map."""
|
|
167
|
+
return load_map_paths(provenance_path)[0]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def load_scope(brief_path: Path, cited: set[str]):
|
|
171
|
+
"""The brief's Scope (skf-classify-changed-files.py load_scope): its
|
|
172
|
+
takes(path) is Category A's in-scope test and its wants(kind) the
|
|
173
|
+
brief's intent. A brief it cannot find or read, or a shared helper it
|
|
174
|
+
cannot load, is bad input (exit 2)."""
|
|
175
|
+
classifier = _shared(CLASSIFIER)
|
|
176
|
+
try:
|
|
177
|
+
return classifier.load_scope(brief_path, cited)
|
|
178
|
+
except ValueError as exc: # a brief it cannot find or read
|
|
179
|
+
_fail(str(exc))
|
|
180
|
+
except Exception as exc: # an out-of-date classifier, or a resolver beside it that cannot load
|
|
181
|
+
_fail(f"cannot load the brief's scope test from {SHARED_SCRIPTS}: {exc}; re-install SKF")
|
|
90
182
|
|
|
91
183
|
|
|
92
184
|
def collect_inventory(detect: dict) -> list[tuple[str, str]]:
|
|
@@ -112,38 +204,82 @@ def collect_inventory(detect: dict) -> list[tuple[str, str]]:
|
|
|
112
204
|
return sorted(seen.items())
|
|
113
205
|
|
|
114
206
|
|
|
115
|
-
def diff(detect: dict, tracked: set[str]
|
|
207
|
+
def diff(detect: dict, tracked: set[str], cited: set[str] | frozenset = frozenset(),
|
|
208
|
+
scope=None) -> dict:
|
|
209
|
+
"""Sort the detected paths into the output lists; `scope` is load_scope's
|
|
210
|
+
Scope (--brief) or None."""
|
|
116
211
|
new_files: list[dict] = []
|
|
117
|
-
|
|
118
|
-
|
|
212
|
+
lists: dict[str, list[str]] = {name: [] for name in (
|
|
213
|
+
"skipped_manual", "already_tracked", "tracked_code", "out_of_scope", "intent_none")}
|
|
119
214
|
|
|
120
215
|
inventory = collect_inventory(detect)
|
|
121
216
|
for sf, kind in inventory:
|
|
122
217
|
if sf in tracked:
|
|
123
|
-
already_tracked.append(sf)
|
|
218
|
+
lists["already_tracked"].append(sf)
|
|
124
219
|
elif _MANUAL_RE.search(sf):
|
|
125
|
-
skipped_manual.append(sf)
|
|
220
|
+
lists["skipped_manual"].append(sf)
|
|
221
|
+
elif sf in cited:
|
|
222
|
+
lists["tracked_code"].append(sf)
|
|
223
|
+
elif scope is not None and not scope.takes(sf):
|
|
224
|
+
lists["out_of_scope"].append(sf)
|
|
225
|
+
elif scope is not None and not scope.wants(kind):
|
|
226
|
+
lists["intent_none"].append(sf)
|
|
126
227
|
else:
|
|
127
228
|
new_files.append({"source_file": sf, "kind": kind})
|
|
128
229
|
|
|
129
230
|
return {
|
|
130
231
|
"new_files": new_files,
|
|
131
|
-
"skipped_manual": sorted(skipped_manual),
|
|
132
|
-
"already_tracked": sorted(already_tracked),
|
|
232
|
+
"skipped_manual": sorted(lists["skipped_manual"]),
|
|
233
|
+
"already_tracked": sorted(lists["already_tracked"]),
|
|
234
|
+
"tracked_code": sorted(lists["tracked_code"]),
|
|
235
|
+
"out_of_scope": sorted(lists["out_of_scope"]),
|
|
236
|
+
"intent_none": sorted(lists["intent_none"]),
|
|
133
237
|
"stats": {
|
|
134
238
|
"inventory_total": len(inventory),
|
|
135
239
|
"new": len(new_files),
|
|
136
|
-
"skipped_manual": len(skipped_manual),
|
|
137
|
-
"already_tracked": len(already_tracked),
|
|
240
|
+
"skipped_manual": len(lists["skipped_manual"]),
|
|
241
|
+
"already_tracked": len(lists["already_tracked"]),
|
|
138
242
|
},
|
|
139
243
|
}
|
|
140
244
|
|
|
141
245
|
|
|
246
|
+
USAGE = "usage: skf-new-file-diff.py <provenance-map-path> [--brief <brief-path>] (detect JSON on stdin)"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class _JsonErrorParser(argparse.ArgumentParser):
|
|
250
|
+
"""Report a usage error as the JSON error and exit 2 every other bad input gets."""
|
|
251
|
+
|
|
252
|
+
def error(self, message: str) -> "NoReturn": # type: ignore[valid-type]
|
|
253
|
+
_fail(f"{USAGE}: {message}")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
257
|
+
parser = _JsonErrorParser(
|
|
258
|
+
prog="skf-new-file-diff.py",
|
|
259
|
+
description=__doc__,
|
|
260
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
261
|
+
allow_abbrev=False,
|
|
262
|
+
)
|
|
263
|
+
parser.add_argument(
|
|
264
|
+
"provenance_map",
|
|
265
|
+
metavar="provenance-map-path",
|
|
266
|
+
help="the skill's provenance-map.json (the detect JSON is read on stdin)",
|
|
267
|
+
)
|
|
268
|
+
parser.add_argument(
|
|
269
|
+
"--brief",
|
|
270
|
+
metavar="brief-path",
|
|
271
|
+
help="the skill brief: keep only the paths its scope takes and its intents want",
|
|
272
|
+
)
|
|
273
|
+
return parser
|
|
274
|
+
|
|
275
|
+
|
|
142
276
|
def main(argv: list[str]) -> int:
|
|
143
|
-
|
|
144
|
-
|
|
277
|
+
parser = _build_parser()
|
|
278
|
+
args = parser.parse_args(argv[1:])
|
|
279
|
+
if args.brief is not None and not args.brief.strip():
|
|
280
|
+
parser.error("--brief is empty (an unbound brief path?): pass the brief, or leave --brief out")
|
|
145
281
|
|
|
146
|
-
provenance_path = Path(
|
|
282
|
+
provenance_path = Path(args.provenance_map)
|
|
147
283
|
if not provenance_path.exists():
|
|
148
284
|
_fail(f"provenance map not found: {provenance_path}")
|
|
149
285
|
|
|
@@ -157,8 +293,9 @@ def main(argv: list[str]) -> int:
|
|
|
157
293
|
if not isinstance(detect, dict):
|
|
158
294
|
_fail(f"detect JSON must be an object; got {type(detect).__name__}")
|
|
159
295
|
|
|
160
|
-
tracked =
|
|
161
|
-
|
|
296
|
+
tracked, cited = load_map_paths(provenance_path)
|
|
297
|
+
scope = load_scope(Path(args.brief), cited) if args.brief is not None else None
|
|
298
|
+
result = diff(detect, tracked, cited, scope)
|
|
162
299
|
print(json.dumps(result, indent=2))
|
|
163
300
|
return 0
|
|
164
301
|
|
|
@@ -7,7 +7,7 @@ description: Pre-code stack feasibility verification against architecture and PR
|
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
Cross-references generated skills against architecture and PRD documents to produce a feasibility report with evidence-backed integration verdicts, coverage analysis, and requirements mapping. Read-only: it reads skills and input documents and writes only the feasibility report (see Workflow Rules).
|
|
10
|
+
Cross-references generated skills against architecture and PRD documents to produce a feasibility report with evidence-backed integration verdicts, coverage analysis, and requirements mapping. Read-only: it reads skills and input documents and writes only the feasibility report, its result files and a scratch run folder (see Workflow Rules).
|
|
11
11
|
|
|
12
12
|
**Schema contract:** This skill is the producer of the SKF shared feasibility report schema — every report conforms to it.
|
|
13
13
|
|
|
@@ -18,6 +18,8 @@ Cross-references generated skills against architecture and PRD documents to prod
|
|
|
18
18
|
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).
|
|
19
19
|
- `{project-root}`-prefixed paths resolve from the project working directory.
|
|
20
20
|
- `{skill-name}` resolves to the skill directory's basename.
|
|
21
|
+
- **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; e.g. `shared/references/output-contract-schema.md` and `shared/health-check.md`.
|
|
22
|
+
- **Sibling skills:** a path that names another SKF skill's folder (`skf-<name>/...`) resolves from the SKF module root, and that skill must be installed with this one.
|
|
21
23
|
|
|
22
24
|
## Role
|
|
23
25
|
|
|
@@ -33,7 +35,11 @@ These rules apply to every step in this workflow:
|
|
|
33
35
|
- If any instruction references a subprocess or tool you lack, achieve the outcome in your main context thread
|
|
34
36
|
- Always communicate in `{communication_language}`
|
|
35
37
|
- At any interactive prompt, the inputs `cancel`, `exit`, `[X]`, `q`, or `:q` exit cleanly with exit code 6 (`halt_reason: "user-cancelled"`)
|
|
36
|
-
- If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
|
|
38
|
+
- If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision; a gate that picks its default for the user (step 1's offer of the newest earlier report) also records that decision in the run sink the moment it decides
|
|
39
|
+
- Every helper a stage runs (a `scripts/` file, or a shared helper from the stage's probe order) is required: if the probe order resolves to no path, or the helper cannot start, HALT (exit code 3, `halt_reason: "resolution-failure"`) at the stage's phase, through the stage's halt envelope. Never recompute a helper's result by hand: the main-thread rule above covers subagent work, not helpers.
|
|
40
|
+
- Every HARD HALT names its exit code, `halt_reason` and phase, and in headless mode prints its envelope through the shared emitter, with the command each stage file shows (`references/exit-codes.md` describes the envelope).
|
|
41
|
+
- Stages 1 to 5 write only the timestamped report `{outputFile}`, and each write that fails halts (exit code 4, `halt_reason: "write-failed"`). Step 6 copies the report to `{outputFileLatest}` only after it passes the feasibility-report check, so a run that halts before then leaves the previous finished copy, the one consumers read, in place.
|
|
42
|
+
- Run state lives in `{run_dir}`, not in context; step 6 deletes it, a HALT keeps it.
|
|
37
43
|
|
|
38
44
|
## Stages
|
|
39
45
|
|
|
@@ -44,7 +50,7 @@ These rules apply to every step in this workflow:
|
|
|
44
50
|
| 3 | Integration Verification | references/integrations.md | Yes |
|
|
45
51
|
| 4 | Requirements Mapping | references/requirements.md | Yes |
|
|
46
52
|
| 5 | Synthesize Verdict | references/synthesize.md | Yes |
|
|
47
|
-
| 6 | Report | references/report.md |
|
|
53
|
+
| 6 | Report | references/report.md | Yes |
|
|
48
54
|
| 7 | Workflow Health Check | references/health-check.md | Yes |
|
|
49
55
|
|
|
50
56
|
## Invocation Contract
|
|
@@ -53,20 +59,14 @@ These rules apply to every step in this workflow:
|
|
|
53
59
|
|--------|--------|
|
|
54
60
|
| **Inputs** | architecture_doc_path [required], prd_path [optional], previous_report_path [optional] |
|
|
55
61
|
| **Flags** | `--headless` / `-H` (auto-resolve all gates); `--architecture-doc <path>` (skip step 1 prompt for the required input); `--prd <path>` (skip step 1 prompt for the optional PRD); `--previous-report <path>` (skip step 1 prompt for delta comparison) |
|
|
56
|
-
| **Gates** | step 1 Input Gate (use args)
|
|
57
|
-
| **Outputs** | `feasibility-report-{
|
|
62
|
+
| **Gates** | step 1 Input Gate (use args), including the offer to compare against the newest earlier report (headless default: use it). No later stage waits for input: a run with 0% coverage or only Blocked pairs prints one warning and continues, so its report still carries the recommendations. |
|
|
63
|
+
| **Outputs** | `feasibility-report-{project_slug}-{timestamp}.md` and its `feasibility-report-{project_slug}-latest.md` copy (not a symlink), written by step 6 once the finished report passes the feasibility-report check, both per the SKF shared feasibility report schema (`{project-root}/_bmad/skf/shared/references/feasibility-report-schema.md`; `{project-root}/src/shared/references/feasibility-report-schema.md` in a dev checkout), with integration verdicts, coverage analysis, recommendations, and evidence sources; plus `verify-stack-result-{YYYYMMDD-HHmmss}.json` and `verify-stack-result-latest.json` in `{forge_data_folder}` |
|
|
58
64
|
| **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true. Per-flag args (`--architecture-doc`, `--prd`, `--previous-report`) consumed at the gates that would otherwise prompt. |
|
|
59
65
|
| **Exit codes** | See `references/exit-codes.md` |
|
|
60
66
|
|
|
61
67
|
## Result Contract (Headless)
|
|
62
68
|
|
|
63
|
-
When `{headless_mode}` is true, step 6
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
SKF_VERIFY_STACK_RESULT_JSON: {"status":"success|error","report_path":"…|null","report_latest_path":"…|null","overall_verdict":"…|null","coverage_percentage":0,"recommendation_count":0,"exit_code":0,"halt_reason":null}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
`status` is `"success"` on the terminal happy path, `"error"` on any halt. `halt_reason` is one of: `null` (success), `"input-missing"`, `"input-invalid"`, `"skills-folder-missing"`, `"insufficient-skills"`, `"forge-folder-unconfigured"`, `"resolution-failure"`, `"previous-report-collision"`, `"inventory-unreliable"`, `"schema-violation"`, `"write-failed"`, `"user-cancelled"`. `exit_code` matches `references/exit-codes.md` (the `analysis-halted` / exit-8 gates are interactive-only, so they never reach this envelope). `overall_verdict` uses the schema tokens (`FEASIBLE`/`CONDITIONALLY_FEASIBLE`/`NOT_FEASIBLE`).
|
|
69
|
+
When `{headless_mode}` is true, step 6 prints one `SKF_VERIFY_STACK_RESULT_JSON: {...}` line on **stdout** and every HARD HALT one on **stderr**, built by the shared emitter: `references/exit-codes.md` gives its fields, the `halt_reason` values and the halt command.
|
|
70
70
|
|
|
71
71
|
## On Activation
|
|
72
72
|
|
|
@@ -74,49 +74,30 @@ SKF_VERIFY_STACK_RESULT_JSON: {"status":"success|error","report_path":"…|null"
|
|
|
74
74
|
- `project_name`, `user_name`, `communication_language`, `document_output_language`
|
|
75
75
|
- `skills_output_folder`, `forge_data_folder`, `sidecar_path`
|
|
76
76
|
|
|
77
|
-
2. **
|
|
78
|
-
- `project_slug` ← slugify `project_name` (lowercase, hyphens only, no unicode, no whitespace)
|
|
77
|
+
2. **Fix the run-scoped variables** so no stage derives them again:
|
|
79
78
|
- `timestamp` ← UTC `YYYYMMDD-HHmmss` captured at activation time
|
|
80
|
-
-
|
|
79
|
+
- `project_slug` is bound by init.md before its §1, from the shared feasibility-report helper, which holds the one slug rule the consumers of this report also apply; never slugify `project_name` by hand
|
|
80
|
+
- The two combine in init.md §4 into `{outputFile}` and `{outputFileLatest}` per the stage frontmatter template, and stay fixed for the whole run, so every later reference to either file resolves to the same path.
|
|
81
|
+
- `run_dir` ← `{project-root}/_bmad-output/.skf-run/skf-verify-stack-{timestamp}`, the run folder init.md creates first
|
|
81
82
|
|
|
82
83
|
3. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `{sidecar_path}/preferences.yaml`. Default: false.
|
|
83
84
|
|
|
85
|
+
**Bind the inputs** in every mode: `{architecture_doc_path}` ← the `--architecture-doc` value, `{prd_path}` ← `--prd` and `{previous_report_path}` ← `--previous-report`, each null when its flag is absent. init.md §1 asks only for those still null.
|
|
86
|
+
|
|
84
87
|
4. **Resolve workflow customization.** Run:
|
|
85
88
|
|
|
86
89
|
```bash
|
|
87
|
-
|
|
88
|
-
--skill {skill-root} --key workflow
|
|
90
|
+
uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --project-root {project-root} --key workflow
|
|
89
91
|
```
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- `{skill-root}/customize.toml` — bundled defaults
|
|
94
|
-
- `_bmad/custom/<skill-name>.toml` under `{project-root}` — team overrides (committed)
|
|
95
|
-
- `_bmad/custom/<skill-name>.user.toml` under `{project-root}` — personal overrides (gitignored)
|
|
96
|
-
|
|
97
|
-
If the script fails or is missing, fall back to reading `{skill-root}/customize.toml` directly — the bundled defaults are an empty string for each path scalar.
|
|
98
|
-
|
|
99
|
-
Apply the path-scalar fallback now so stage files don't have to repeat the conditional logic. For each of the four scalars, if the merged value is empty or absent, use the bundled default:
|
|
93
|
+
It merges the bundled `{skill-root}/customize.toml` with `{project-root}/_bmad/custom/skf-verify-stack.toml` (team overrides, committed) and `.user.toml` (personal overrides, gitignored). When it exits non-zero, prints no JSON or is missing, print one line, `[activation/warn] customization_resolver_unavailable: <reason>` (`<reason>`: its first stderr line, `not found` when the script is missing, `no JSON` when it printed none). If the resolver cannot run, read `{skill-root}/customize.toml` alone and use its bundled defaults: the `{project-root}/_bmad/custom/` overrides do not apply to this run. In that case keep the reason as `{customization_resolver_unavailable}` (unset when the resolver ran) for init.md to record once it has created `{run_dir}`.
|
|
100
94
|
|
|
101
|
-
- `{reportTemplatePath}` ← `workflow.report_template_path` if non-empty, else `assets/feasibility-report-template.md`
|
|
102
|
-
- `{integrationRulesPath}` ← `workflow.integration_rules_path` if non-empty, else `references/integration-verification-rules.md`
|
|
103
|
-
- `{coveragePatternsPath}` ← `workflow.coverage_patterns_path` if non-empty, else `references/coverage-patterns.md`
|
|
104
|
-
- `{outputFolderPath}` ← `workflow.output_folder_path` if non-empty, else `{forge_data_folder}`
|
|
105
|
-
|
|
106
|
-
Stash all four as workflow-context variables. Stage files reference them directly — no conditional at the usage site. Empty-string overrides cleanly fall through to the bundled default.
|
|
95
|
+
Apply the path-scalar fallback now so stage files don't repeat it: `{reportTemplatePath}` ← `workflow.report_template_path` if non-empty, else `assets/feasibility-report-template.md`. An empty or absent value falls through to that default, and init.md §4 loads the variable as it is. Bind `{outputFolderPath}` ← `{forge_data_folder}`, always (no setting moves it): the report and its `-latest` copy go where create-stack-skill and refine-architecture look for them, and step 1 finds the earlier reports there.
|
|
107
96
|
|
|
108
97
|
The same merge resolves `workflow.on_complete` (default empty = no-op); report.md §5 executes it, if non-empty, at the terminal stage.
|
|
109
98
|
|
|
110
|
-
Also apply the array surfaces so they are not silent no-ops: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (`file:`-prefixed entries load their file/glob contents as facts
|
|
111
|
-
|
|
112
|
-
5. **Pre-flight write probe.** Verify `{outputFolderPath}` is writable. A read-only mount, full disk, or permissions-denied path otherwise only surfaces at init.md §4 atomic write — by then the user has already gone through the input prompts:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
mkdir -p "{outputFolderPath}" && \
|
|
116
|
-
printf 'probe' > "{outputFolderPath}/.skf-write-probe" && \
|
|
117
|
-
rm "{outputFolderPath}/.skf-write-probe"
|
|
118
|
-
```
|
|
99
|
+
Also apply the array surfaces so they are not silent no-ops: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (`file:`-prefixed entries load their file/glob contents as facts: the bundled default loads every `project-context.md` under `{project-root}`; an entry prefixed `!` drops each earlier entry it names and loads nothing itself, so an override's `"!file:{project-root}/**/project-context.md"` turns that default off); then execute each entry in `workflow.activation_steps_append` after activation completes.
|
|
119
100
|
|
|
120
|
-
|
|
101
|
+
5. **Pre-flight: the emitter.** Before the first prompt, resolve `{emitEnvelopeHelper}` ← `{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py`, else `{project-root}/src/shared/scripts/skf-emit-result-envelope.py` (the first that exists). If neither exists, HALT (exit code 3, `halt_reason: "resolution-failure"`) at phase `on-activation:emitter` and display only: "Verify Stack cannot run without `skf-emit-result-envelope.py`, which is not installed. Re-install SKF."
|
|
121
102
|
|
|
122
103
|
6. Load, read the full file, and then execute `references/init.md` to begin the workflow.
|
|
@@ -19,6 +19,9 @@ architectureDoc: ''
|
|
|
19
19
|
prdDoc: ''
|
|
20
20
|
previousReport: ''
|
|
21
21
|
skillsAnalyzed: 0
|
|
22
|
+
coverageCovered: null
|
|
23
|
+
coverageMissing: null
|
|
24
|
+
coverageReplaced: null
|
|
22
25
|
stepsCompleted: []
|
|
23
26
|
requirementsPass: ''
|
|
24
27
|
requirementsFulfilled: null
|
|
@@ -55,9 +58,8 @@ deltaUnchanged: null
|
|
|
55
58
|
|
|
56
59
|
## Integration Verdicts
|
|
57
60
|
|
|
58
|
-
<!--
|
|
59
|
-
|
|
60
|
-
The table header is fixed and MUST be emitted exactly as shown below: -->
|
|
61
|
+
<!-- Filled in place by integrations: one row per pair goes under this header.
|
|
62
|
+
The report holds this table once: consumers read it and reject a second copy. -->
|
|
61
63
|
|
|
62
64
|
| lib_a | lib_b | verdict | rationale |
|
|
63
65
|
|-------|-------|---------|-----------|
|
|
@@ -72,5 +74,9 @@ The table header is fixed and MUST be emitted exactly as shown below: -->
|
|
|
72
74
|
|
|
73
75
|
## Evidence Sources
|
|
74
76
|
|
|
75
|
-
<!--
|
|
76
|
-
|
|
77
|
+
<!-- Filled in place by synthesize: one row per skill goes under this header (the
|
|
78
|
+
next run's delta reads its evidence_tier column), then the architecture and PRD
|
|
79
|
+
document paths. -->
|
|
80
|
+
|
|
81
|
+
| skill | evidence_tier | confidence_tier | metadata_schema_version | skill_md |
|
|
82
|
+
|-------|---------------|-----------------|-------------------------|----------|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# DO NOT EDIT -- overwritten on every update.
|
|
2
2
|
#
|
|
3
3
|
# Workflow customization surface for skf-verify-stack.
|
|
4
|
-
# Team overrides: _bmad/custom/skf-verify-stack.toml
|
|
5
|
-
# Personal overrides: _bmad/custom/skf-verify-stack.user.toml
|
|
4
|
+
# Team overrides: {project-root}/_bmad/custom/skf-verify-stack.toml
|
|
5
|
+
# Personal overrides: {project-root}/_bmad/custom/skf-verify-stack.user.toml
|
|
6
6
|
|
|
7
7
|
[workflow]
|
|
8
8
|
|
|
@@ -10,27 +10,41 @@
|
|
|
10
10
|
# scalars: override wins • arrays (persistent_facts, activation_steps_*): append
|
|
11
11
|
# arrays-of-tables with `code`/`id`: replace matching items, append new ones.
|
|
12
12
|
|
|
13
|
-
# Steps
|
|
14
|
-
#
|
|
15
|
-
#
|
|
13
|
+
# Steps SKILL.md On Activation step 4 runs once this file resolves: after
|
|
14
|
+
# config.yaml is loaded and {headless_mode} is resolved, before the emitter
|
|
15
|
+
# pre-flight (step 5) and before any stage. Overrides append. Use for org-wide
|
|
16
|
+
# pre-flight checks (auth, network, compliance) that must precede any
|
|
17
|
+
# verification work.
|
|
16
18
|
|
|
17
19
|
activation_steps_prepend = []
|
|
18
20
|
|
|
19
|
-
# Steps to run
|
|
20
|
-
#
|
|
21
|
-
#
|
|
21
|
+
# Steps to run once activation completes (the emitter resolved), before
|
|
22
|
+
# references/init.md (the first stage) loads. Overrides append. Use for
|
|
23
|
+
# context loads or banner customization.
|
|
22
24
|
|
|
23
25
|
activation_steps_append = []
|
|
24
26
|
|
|
25
27
|
# Persistent facts the workflow keeps in mind for the whole run
|
|
26
28
|
# (verification standards, evidence-citation rules, house-style verdict
|
|
27
|
-
# language). Overrides append.
|
|
29
|
+
# language, the other names a technology goes by). Overrides append.
|
|
28
30
|
#
|
|
29
|
-
# Each entry is
|
|
31
|
+
# Each entry is one of:
|
|
30
32
|
# - a literal sentence, e.g. "Verdict citations must include file:line spans."
|
|
31
33
|
# - a file reference prefixed with `file:`, e.g.
|
|
32
34
|
# "file:{project-root}/docs/verify-policy.md" (globs supported; file
|
|
33
35
|
# contents are loaded and treated as facts).
|
|
36
|
+
# - an entry prefixed with `!`, which loads nothing and drops each earlier
|
|
37
|
+
# entry it names.
|
|
38
|
+
#
|
|
39
|
+
# Extra aliases go here: a fact such as "Our documents call the postgresql
|
|
40
|
+
# skill Postgres or PG." makes step 2 (coverage) pass both names to the
|
|
41
|
+
# mentions helper as aliases of that skill, so the architecture document's
|
|
42
|
+
# "PG" covers it and pairs it for step 3.
|
|
43
|
+
#
|
|
44
|
+
# The default below loads every project-context.md under {project-root}.
|
|
45
|
+
# Overrides append, so they cannot delete it; to stop loading those files,
|
|
46
|
+
# set in {project-root}/_bmad/custom/skf-verify-stack.toml:
|
|
47
|
+
# persistent_facts = ["!file:{project-root}/**/project-context.md"]
|
|
34
48
|
|
|
35
49
|
persistent_facts = [
|
|
36
50
|
"file:{project-root}/**/project-context.md",
|
|
@@ -42,18 +56,37 @@ persistent_facts = [
|
|
|
42
56
|
|
|
43
57
|
on_complete = ""
|
|
44
58
|
|
|
45
|
-
# --- Optional
|
|
46
|
-
#
|
|
47
|
-
# Lift the canonical asset paths so orgs can substitute house-style copies
|
|
48
|
-
# without forking the skill. Empty string = use the bundled default.
|
|
59
|
+
# --- Optional override. An empty string means the default named below. ---
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
# The template step 1 (references/init.md section 4) creates the feasibility
|
|
62
|
+
# report from: a path from the skill root, or a {project-root}/... path.
|
|
63
|
+
# Default: assets/feasibility-report-template.md. A house-style copy may
|
|
64
|
+
# reword the prose and add sections of its own, and must keep what the shared
|
|
65
|
+
# feasibility report schema (feasibility-report-schema.md under
|
|
66
|
+
# shared/references/) fixes, since the next run, create-stack-skill and
|
|
67
|
+
# refine-architecture read the report through it:
|
|
68
|
+
# - the frontmatter keys with their starting values, `schemaVersion: "1.0"`
|
|
69
|
+
# included;
|
|
70
|
+
# - the `## Executive Summary`, `## Coverage Analysis`,
|
|
71
|
+
# `## Integration Verdicts`, `## Recommendations` and `## Evidence Sources`
|
|
72
|
+
# headings, once each and in that order;
|
|
73
|
+
# - one empty `| lib_a | lib_b | verdict | rationale |` table under
|
|
74
|
+
# `## Integration Verdicts`, and the empty
|
|
75
|
+
# `| skill | evidence_tier | confidence_tier | metadata_schema_version | skill_md |`
|
|
76
|
+
# table under `## Evidence Sources`: the stages fill both in place.
|
|
77
|
+
# Step 6 checks the finished report against the schema and halts
|
|
78
|
+
# (`schema-violation`) on a copy that breaks it.
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
# use {forge_data_folder} from config.yaml. The result-contract JSON
|
|
56
|
-
# (verify-stack-result-*.json) always stays in {forge_data_folder} as a stable
|
|
57
|
-
# path for pipeline consumers, so redirecting this does not relocate it.
|
|
80
|
+
report_template_path = ""
|
|
58
81
|
|
|
59
|
-
|
|
82
|
+
# Not overridable:
|
|
83
|
+
# - the integration verification rules: they hold the obligations the shared
|
|
84
|
+
# schema puts on this report's producer (Check 4's literal citation and the
|
|
85
|
+
# Plausible cap), which the report's readers rely on;
|
|
86
|
+
# - the coverage patterns: the shared mentions helper finds the skills the
|
|
87
|
+
# architecture document names, and a fact (see persistent_facts) adds the
|
|
88
|
+
# other names a skill goes by;
|
|
89
|
+
# - the report folder: the report always lands in {forge_data_folder}, where
|
|
90
|
+
# create-stack-skill and refine-architecture look for it. Change
|
|
91
|
+
# forge_data_folder in config.yaml to move the report and its readers
|
|
92
|
+
# together.
|