bmad-module-skill-forge 2.1.0 → 3.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 +51 -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 +135 -18
- package/docs/verifying-a-skill.md +83 -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 +2 -2
- 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 +32 -12
- package/src/shared/references/description-guard-protocol.md +14 -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 +30 -14
- package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
- 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 +694 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
- 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 +3 -2
- package/src/shared/scripts/skf-description-guard.py +117 -9
- package/src/shared/scripts/skf-detect-docs.py +892 -4
- 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 +1 -1
- package/src/shared/scripts/skf-detect-tools.py +215 -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 +4084 -85
- package/src/shared/scripts/skf-extraction-inventory.py +746 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +539 -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 +444 -203
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +661 -0
- package/src/shared/scripts/skf-hash-content.py +166 -12
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +159 -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 +1 -1
- 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 +161 -27
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
- 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 +128 -13
- 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 +670 -119
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +5 -5
- 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 +4 -1
- 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 +2919 -54
- package/src/shared/scripts/skf-write-skill-brief.py +367 -19
- 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 +27 -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/init.md +148 -210
- package/src/skf-audit-skill/references/re-index.md +96 -108
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +27 -18
- 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 +75 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
- package/src/skf-brief-skill/SKILL.md +36 -25
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +17 -24
- package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +142 -125
- package/src/skf-brief-skill/references/confirm-brief.md +10 -8
- 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 +24 -80
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +3 -2
- 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 +6 -5
- package/src/skf-create-skill/assets/skill-sections.md +17 -6
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
- 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 +107 -29
- 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 +20 -0
- package/src/skf-create-skill/references/extract.md +200 -161
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
- package/src/skf-create-skill/references/extraction-patterns.md +584 -196
- 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 +28 -59
- package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
- 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 +55 -18
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -57
- 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 +79 -146
- 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 +118 -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 -89
- package/src/skf-setup/references/detect-and-tier.md +58 -66
- 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 +217 -254
- 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 +163 -88
- 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 +162 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +41 -155
- package/src/skf-test-skill/references/source-access-protocol.md +19 -92
- 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 +287 -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 +1381 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
- package/src/skf-test-skill/scripts/score-signatures.py +459 -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 +127 -52
- 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 +233 -222
- 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 +250 -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 +180 -47
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +100 -35
- package/src/skf-update-skill/references/write.md +331 -149
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
- 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 -645
- 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,39 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'report.md'
|
|
3
|
-
|
|
4
|
-
managedSectionLogic: 'skf-export-skill/assets/managed-section-format.md'
|
|
5
|
-
# Resolve `{manifestOpsHelper}` by probing `{manifestOpsProbeOrder}` in
|
|
6
|
-
# order (installed SKF module path first, src/ dev-checkout fallback);
|
|
7
|
-
# first existing path wins. §2 calls it for atomic manifest deprecate /
|
|
8
|
-
# remove with v1→v2 migration handled internally — letting the LLM hand-
|
|
9
|
-
# roll JSON manipulation risks key-order drift, indent regressions, and
|
|
10
|
-
# write-atomicity bugs. HALT if neither candidate exists.
|
|
11
|
-
manifestOpsProbeOrder:
|
|
12
|
-
- '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
|
|
13
|
-
- '{project-root}/src/shared/scripts/skf-manifest-ops.py'
|
|
14
|
-
# Resolve `{rebuildManagedSectionsHelper}` similarly. §3 calls it (replace
|
|
15
|
-
# action) for the surgical between-marker rewrite — the LLM still computes
|
|
16
|
-
# the new managed section text, but the file mutation is deterministic
|
|
17
|
-
# (atomic temp-file + rename, marker preservation, post-write verify).
|
|
18
|
-
rebuildManagedSectionsProbeOrder:
|
|
19
|
-
- '{project-root}/_bmad/skf/shared/scripts/skf-rebuild-managed-sections.py'
|
|
20
|
-
- '{project-root}/src/shared/scripts/skf-rebuild-managed-sections.py'
|
|
21
|
-
# Resolve `{updateActiveSymlinkHelper}` similarly. §4 uses it (update
|
|
22
|
-
# action) to atomically repoint `{skill_group}/active` after a version-level
|
|
23
|
-
# purge deletes the version the symlink pointed at — the helper does a
|
|
24
|
-
# temp-symlink + os.replace flip so concurrent readers never see a missing
|
|
25
|
-
# link. Matches skf-update-skill/references/write.md and
|
|
26
|
-
# skf-rename-skill/references/execute.md. HALT if neither candidate exists.
|
|
3
|
+
# {updateActiveSymlinkHelper}: the §4 `active` repoint.
|
|
27
4
|
updateActiveSymlinkProbeOrder:
|
|
28
5
|
- '{project-root}/_bmad/skf/shared/scripts/skf-update-active-symlink.py'
|
|
29
6
|
- '{project-root}/src/shared/scripts/skf-update-active-symlink.py'
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
#
|
|
7
|
+
# {skillInventoryHelper}: the §4 guarded delete and `resolve`.
|
|
8
|
+
skillInventoryProbeOrder:
|
|
9
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-skill-inventory.py'
|
|
10
|
+
- '{project-root}/src/shared/scripts/skf-skill-inventory.py'
|
|
11
|
+
# {dropRosterHelper}: the §5 context-file check.
|
|
12
|
+
dropRosterHelper: 'scripts/drop-roster.py'
|
|
13
|
+
# {dirSizesHelper}: the §4 `disk_freed` label.
|
|
37
14
|
dirSizesHelper: 'scripts/dir-sizes.py'
|
|
38
15
|
---
|
|
39
16
|
|
|
@@ -51,24 +28,27 @@ Execute the drop decisions recorded in step 1: update the export manifest, rebui
|
|
|
51
28
|
- Do not re-prompt the user — decisions were made in step 1
|
|
52
29
|
- Do not delete files in deprecate mode; do not widen deletion scope beyond `affected_directories`
|
|
53
30
|
- Report each stage's outcome as it completes
|
|
31
|
+
- Record each degraded outcome as a warning the moment it happens, with the text the section names, so the envelope and the result record carry it: `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning '<the warning>'`, a single quote in it written as a backtick. If the command fails, go on: only that entry is lost.
|
|
54
32
|
|
|
55
33
|
## MANDATORY SEQUENCE
|
|
56
34
|
|
|
57
|
-
### 1.
|
|
35
|
+
### 1. Halt Envelope
|
|
58
36
|
|
|
59
|
-
|
|
37
|
+
Every HALT in this step names its exit code, `halt_reason` and phase. In headless mode it also prints its envelope through the shared emitter before it stops (`{emitEnvelopeHelper}` and `{run_dir}` come from SKILL.md On-Activation §4): stage `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "exit_code": <code>}` plus the envelope fields the site names (and `"path"` when the halt names one), then run
|
|
60
38
|
|
|
61
|
-
|
|
39
|
+
```bash
|
|
40
|
+
uv run {emitEnvelopeHelper} emit-halt --workflow skf-drop-skill --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
|
|
41
|
+
```
|
|
62
42
|
|
|
63
|
-
|
|
43
|
+
and display the line it prints verbatim. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. The emitter gives every field the site leaves out its default (`null`, `[]` or `false`) and checks `exit_code` against `halt_reason`. If it exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message, emits nothing and then deletes the run folder (`rm -rf "{run_dir}"`), which nothing reads after it.
|
|
64
44
|
|
|
65
45
|
### 2. Update Export Manifest
|
|
66
46
|
|
|
67
|
-
**If `target_in_manifest == false`** (draft skill discovered only by on-disk scan): Skip this section entirely. There is no manifest entry to deprecate or delete. Set `manifest_updated = false` and proceed directly to section 3.
|
|
47
|
+
**If `target_in_manifest == false`** (draft skill discovered only by on-disk scan): Skip this section entirely. There is no manifest entry to deprecate or delete. Set `manifest_updated = false` and proceed directly to section 3.
|
|
68
48
|
|
|
69
49
|
**If `target_in_manifest == true`:**
|
|
70
50
|
|
|
71
|
-
|
|
51
|
+
Write through the `{manifestOpsHelper}` On-Activation §4 resolved: atomic manifest mutation always goes through the helper.
|
|
72
52
|
|
|
73
53
|
**If `is_skill_level == false` (version-level drop):**
|
|
74
54
|
|
|
@@ -82,88 +62,56 @@ The helper sets `exports.{target_skill}.versions.{version}.status = "deprecated"
|
|
|
82
62
|
|
|
83
63
|
**If `is_skill_level == true` (skill-level drop):**
|
|
84
64
|
|
|
85
|
-
|
|
86
|
-
python3 {manifestOpsHelper} {skills_output_folder} remove {target_skill}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
The helper deletes the `exports.{target_skill}` key entirely; other entries are untouched.
|
|
90
|
-
|
|
91
|
-
Set context flag `manifest_updated = true`.
|
|
92
|
-
|
|
93
|
-
**On error (helper non-zero exit):**
|
|
65
|
+
- **Deprecate (`drop_mode == "deprecate"`):** mark every version deprecated and keep the entry, so the soft drop stays reversible. With no version, the helper sets the `status` of every version under `exports.{target_skill}.versions` to `"deprecated"` and leaves `active_version` as it is:
|
|
94
66
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
67
|
+
```bash
|
|
68
|
+
python3 {manifestOpsHelper} {skills_output_folder} deprecate {target_skill}
|
|
69
|
+
```
|
|
98
70
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Load the `ides` list from `config.yaml`. The installer writes IDE identifiers — these must be mapped to context files and skill roots using the "IDE → Context File Mapping" table in `{managedSectionLogic}`.
|
|
102
|
-
|
|
103
|
-
**Resolve `target_context_files`** using the canonical mapping table in `{managedSectionLogic}`:
|
|
104
|
-
|
|
105
|
-
1. For each entry in `config.yaml.ides`, look up its `context_file` and `skill_root` from the mapping table
|
|
106
|
-
2. For any entry not found in the table, default to `{unknownIdeDefaultContextFile}` / `{unknownIdeDefaultSkillRoot}` (resolved at SKILL.md On Activation §3 from `workflow.unknown_ide_default_*`, bundled defaults `AGENTS.md` / `.agents/skills/`) and emit a warning: "Unknown IDE '{value}' in config.yaml — defaulting to {unknownIdeDefaultContextFile}"
|
|
107
|
-
3. Deduplicate by `context_file` — when multiple IDEs map to the same context file, use the first configured IDE's `skill_root`
|
|
108
|
-
4. If `config.yaml.ides` is absent or the mapping yields an empty list, fall back to `[{context_file: "{unknownIdeDefaultContextFile}", skill_root: "{unknownIdeDefaultSkillRoot}"}]` and emit a note: "No IDEs configured in config.yaml — defaulting to {unknownIdeDefaultContextFile}"
|
|
71
|
+
- **Purge:** the helper deletes the `exports.{target_skill}` key entirely; other entries are untouched:
|
|
109
72
|
|
|
110
|
-
|
|
73
|
+
```bash
|
|
74
|
+
python3 {manifestOpsHelper} {skills_output_folder} remove {target_skill}
|
|
75
|
+
```
|
|
111
76
|
|
|
112
|
-
|
|
77
|
+
When the helper exits 0 (`status: "ok"`), set context flag `manifest_updated = true`.
|
|
113
78
|
|
|
114
|
-
|
|
79
|
+
**On error (the helper exits non-zero):** HALT (exit code 4, `halt_reason: "manifest-write-failed"`, phase `execute:manifest-write`, path `{skills_output_folder}/.export-manifest.json`) in every mode, before section 3: "**Manifest update failed:** {the helper's `error`, `status: not_found` when the entry is no longer there, or its stderr when stdout holds no JSON}. Nothing was changed: no file was deleted, no context file was rebuilt, and the manifest is in its pre-drop state. Re-run the drop once the cause is fixed." The §1 halt envelope carries the resolved `skill`, `drop_mode` and `versions_affected`, with `manifest_updated: false`. The run ends here: step 3 never runs, so no success report is shown and no result file is written.
|
|
115
80
|
|
|
116
|
-
|
|
117
|
-
- If the file does not exist, skip this context file (nothing to rebuild — the file will be re-created next time export-skill runs)
|
|
118
|
-
- If the file exists but contains no `<!-- SKF:BEGIN -->` marker, skip this context file (no managed section to rewrite)
|
|
119
|
-
- If the file contains `<!-- SKF:BEGIN -->` but no matching `<!-- SKF:END -->`, record the error against that context file and continue to the next entry — do not halt the entire drop on a malformed context file. The manifest has already been updated in section 2 and is canonical state; the context file can be repaired manually and rebuilt on the next `[EX] Export Skill` run.
|
|
81
|
+
### 3. Rebuild Context Files
|
|
120
82
|
|
|
121
|
-
|
|
122
|
-
- Read the manifest's `exports` object (already updated in section 2)
|
|
123
|
-
- For each skill, resolve its `active_version`
|
|
124
|
-
- If `versions.{active_version}.status == "deprecated"`, skip that skill entirely
|
|
125
|
-
- The result is the set of `{skill-name, active_version}` pairs that should appear in the managed section
|
|
83
|
+
Rebuild the managed section of each context file step 1 resolved (`target_context_files`, from `resolve-targets` in select.md §9b) the way export-skill writes it, through the `{rebuildManagedSectionsHelper}` On-Activation §4 resolved.
|
|
126
84
|
|
|
127
|
-
|
|
128
|
-
- For each `{skill-name, active_version}` in the set, read `{skills_output_folder}/{skill-name}/{active_version}/{skill-name}/context-snippet.md`
|
|
129
|
-
- If the file is missing, fall back to the `active` symlink path, then skip with a warning if still not found
|
|
130
|
-
- Collect successful snippets into the skill index
|
|
85
|
+
A target's file is `{context_path}`, its `{context_file}` in `{project-root}`, and `{target_paths}` is the `{context_path}` of every target, each quoted, space-separated, in the order of `target_context_files`. For each entry in `target_context_files`:
|
|
131
86
|
|
|
132
|
-
|
|
87
|
+
1. **Check the file.**
|
|
133
88
|
|
|
134
|
-
|
|
89
|
+
```bash
|
|
90
|
+
python3 {rebuildManagedSectionsHelper} "{context_path}" check
|
|
91
|
+
```
|
|
135
92
|
|
|
136
|
-
|
|
93
|
+
Its `case` decides. `create` (no file) or `append` (no managed section): skip this file, since there is no section to rebuild (export-skill adds one on its next run). `malformed` or `unreadable`: bind `{context_error}` ← `error` and take item 4. The drop does not halt on one context file: section 2 already updated the manifest, which is the canonical state, and the next `[EX] Export Skill` run rebuilds the file once it is repaired. `regenerate`: continue.
|
|
137
94
|
|
|
138
|
-
|
|
95
|
+
2. **Build the section body.**
|
|
139
96
|
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
|
146
|
-
|{skill-snippet-1}
|
|
147
|
-
|
|
|
148
|
-
|{skill-snippet-2}
|
|
149
|
-
|
|
|
150
|
-
|{skill-snippet-N}
|
|
151
|
-
<!-- SKF:END -->
|
|
97
|
+
```bash
|
|
98
|
+
python3 {rebuildManagedSectionsHelper} assemble "{context_path}" \
|
|
99
|
+
--skills-folder "{skills_output_folder}" --skill-root "{skill_root}" \
|
|
100
|
+
--dropped {target_skill} --orphan-sources {target_paths} \
|
|
101
|
+
[--skill-root-override "{snippet_skill_root_override}"]
|
|
152
102
|
```
|
|
153
103
|
|
|
154
|
-
|
|
104
|
+
Add `--skill-root-override` only when `snippet_skill_root_override` is set in `config.yaml`. The helper builds the body export-skill would write for the manifest section 2 left, with the rows of skills the manifest does not know (orphan rows) kept verbatim; `--dropped {target_skill}` keeps the dropped skill's old rows from staying as orphan rows. It writes the body to `{context_path}.skf-content` and prints its result as JSON: show each `warnings[]` line and each `skipped_*` entry as a warning, except a `skipped_deprecated` entry for `{target_skill}`, which the drop itself deprecated. On a non-zero exit, bind `{context_error}` ← `error` and take item 4.
|
|
155
105
|
|
|
156
|
-
|
|
106
|
+
3. **Replace the section.** Feed the staged body to the helper by stdin redirection. Never pass the body inline as `--content "…"` or through `echo`: snippets carry backticks, `$` and quotes, which the shell expands, and the helper checks the text it received, so it would report success on corrupted bytes.
|
|
157
107
|
|
|
158
108
|
```bash
|
|
159
|
-
python3 {rebuildManagedSectionsHelper} {
|
|
109
|
+
python3 {rebuildManagedSectionsHelper} "{context_path}" replace < "{context_path}.skf-content"
|
|
160
110
|
```
|
|
161
111
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
9. **Verify (deferred to helper).** The `replace` action above performs verification internally. Treat any non-zero exit code as a per-file failure (next bullet). If the helper is missing entirely (no probe candidate exists), HALT (exit code 4, `halt_reason: "context-rebuild-failed"`) — the rewrite cannot proceed without the atomic helper.
|
|
112
|
+
Delete `{context_path}.skf-content` once the helper returns, whatever its exit code. It refuses a body that holds a marker of its own, and then leaves the file unchanged. It exits 0 only when the `status` of its JSON is `"ok"`. On a non-zero exit, bind `{context_error}` ← `error`, or the helper's stderr when stdout holds no JSON.
|
|
165
113
|
|
|
166
|
-
|
|
114
|
+
4. **On per-file failure:** record `{context_error}` against that context file, record the warning `context_rebuild_failed: {context_file}: {context_error}`, and continue to the next entry. Do not halt: other context files should still be rebuilt.
|
|
167
115
|
|
|
168
116
|
**After the loop,** record `context_files_updated` as the list of files that were successfully rewritten, and `context_files_failed` as the list of any that failed.
|
|
169
117
|
|
|
@@ -175,84 +123,69 @@ Report: "**Rebuilt managed sections in:** {list of updated files}. {if any faile
|
|
|
175
123
|
|
|
176
124
|
**If `drop_mode == "purge"`:**
|
|
177
125
|
|
|
178
|
-
1.
|
|
179
|
-
|
|
180
|
-
2. **Measure sizes before deleting anything.** Run the sizing helper once over `affected_directories` so each path's byte size is captured while it still exists:
|
|
126
|
+
1. **Delete through the guarded delete.** Resolve `{skillInventoryHelper}` from `{skillInventoryProbeOrder}` (first existing path wins) and run it once over every path in `affected_directories`, with the skills folder and the forge folder as the only folders it may delete inside:
|
|
181
127
|
|
|
182
128
|
```bash
|
|
183
|
-
uv run {
|
|
129
|
+
uv run {skillInventoryHelper} guarded-delete --root "{skills_output_folder}" --root "{forge_data_folder}" {each path in affected_directories, quoted, space-separated}
|
|
184
130
|
```
|
|
185
131
|
|
|
186
|
-
|
|
132
|
+
The helper removes any trailing `/`, and SKF never deletes through a link. Bind `files_deleted` ← `files_deleted`, `delete_failures` ← `delete_failures` (each `{path, error}`), `{bytes_freed}` ← `bytes_freed` and `{purge_status}` ← `purge_status`. An empty `affected_directories` (a manifest entry whose folders are already gone) deletes nothing and returns `success`.
|
|
187
133
|
|
|
188
|
-
|
|
189
|
-
a. Verify the path is inside either `{skills_output_folder}` or `{forge_data_folder}` (defense in depth against accidental deletion of unrelated paths)
|
|
190
|
-
b. If the directory does not exist, record it as "(already absent)" and continue
|
|
191
|
-
c. Delete the directory recursively
|
|
192
|
-
d. Verify deletion succeeded (the path no longer exists)
|
|
193
|
-
e. Append the path to `files_deleted`
|
|
134
|
+
When no candidate exists, delete nothing by hand: take the `failed` outcome below, with "the inventory helper that checks each delete is missing; re-install SKF" as the error of every path. On a non-zero exit, or no JSON on stdout, take the `failed` outcome too, with the helper's `error` (its stderr when stdout holds no JSON) as the error of every path.
|
|
194
135
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
136
|
+
2. **Version-level purge, single version:** if the `active` symlink pointed to the just-deleted version, update or remove it. The version folder is already gone, so a symlink problem never claims `delete-failed`: record the manual repair in `verification_errors`, record the warning `active_link_dangling: {skills_output_folder}/{target_skill}/active: {the manual repair}`, and continue. Read the versions the drop left through the inventory helper's `resolve`, never from the manifest by hand:
|
|
137
|
+
```bash
|
|
138
|
+
uv run {skillInventoryHelper} resolve "{skills_output_folder}" --skill {target_skill} --forge-data-folder "{forge_data_folder}"
|
|
139
|
+
```
|
|
140
|
+
When it exits non-zero, record the manual repair (point `active` at the newest version the manifest does not list as deprecated, or remove it when none is left) as above and continue.
|
|
141
|
+
- **Other non-deprecated versions remain** (`resolve.counts.non_deprecated` is above `0`): `{new_active_version}` ← `resolve.active_version`, the version the manifest lists as active, unless it is null or its `resolve.versions` entry has `status` `"deprecated"`; then `resolve.newest_non_deprecated`. Repoint `active` to it atomically through the shared helper rather than a hand-rolled `ln`: resolve `{updateActiveSymlinkHelper}` from `{updateActiveSymlinkProbeOrder}` (first existing path wins), then:
|
|
199
142
|
```bash
|
|
200
143
|
python3 {updateActiveSymlinkHelper} update \
|
|
201
144
|
--skill-group {skills_output_folder}/{target_skill} \
|
|
202
145
|
--version {new_active_version}
|
|
203
146
|
```
|
|
204
|
-
|
|
205
|
-
- **No non-deprecated versions remain** (reachable only when dropping the sole surviving version, permitted in step 1 because no other non-deprecated versions existed): remove the now-dangling `active` symlink with a single atomic unlink of the link itself
|
|
206
|
-
|
|
207
|
-
5. **Skill-level purge:**
|
|
208
|
-
- `{skills_output_folder}/{target_skill}/` and `{forge_data_folder}/{target_skill}/` are deleted in full — the `active` symlink disappears with the parent directory
|
|
147
|
+
Record a `mismatch`/`missing-target` exit (code 2), or a missing helper (no probe candidate), as above with the manual fix `ln -sfn {new_active_version} {skills_output_folder}/{target_skill}/active`, and continue.
|
|
148
|
+
- **No non-deprecated versions remain** (`resolve.counts.non_deprecated` is `0`, reachable only when dropping the sole surviving version, permitted in step 1 because no other non-deprecated versions existed): remove the now-dangling `active` symlink with a single atomic unlink of the link itself: `rm {skills_output_folder}/{target_skill}/active` (unlink removes only the symlink, never its target). When it fails, record it as above with the manual fix `rm {skills_output_folder}/{target_skill}/active`.
|
|
209
149
|
|
|
210
|
-
|
|
150
|
+
3. Format the size of the deleted folders through the sizing helper; do not add or round in-prompt:
|
|
211
151
|
|
|
212
152
|
```bash
|
|
213
|
-
uv run {dirSizesHelper} humanize {
|
|
153
|
+
uv run {dirSizesHelper} humanize {bytes_freed}
|
|
214
154
|
```
|
|
215
155
|
|
|
216
156
|
Store `result.total_human` as `disk_freed` (e.g. `"4.2 MB"`; `"0 B"` when nothing was deleted).
|
|
217
157
|
|
|
218
|
-
**
|
|
158
|
+
**Classify the deletion outcome from `{purge_status}`:**
|
|
219
159
|
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
|
|
224
|
-
**After the loop — classify the deletion outcome.** Let `attempted` be the number of paths in `affected_directories` that existed on disk (i.e. were not recorded as "(already absent)"):
|
|
225
|
-
|
|
226
|
-
- **Full purge failure** — `attempted > 0` AND every attempted path is in `delete_failures` (nothing was deleted): the purge accomplished none of its destructive intent, so it must NOT report success. HALT (exit code 4, `halt_reason: "delete-failed"`): "**Purge failed** — none of the {attempted} target director(ies) could be deleted: {list each path with its error}. The manifest and context files were already updated in sections 2–3; the on-disk files remain and can be removed manually (`rm -rf {path}`)." In headless mode, emit the error envelope per `{headlessContract}` with the resolved `skill`, `drop_mode`, `versions_affected`, `files_deleted: []`, and `manifest_updated` from section 2. Do not proceed to section 5.
|
|
227
|
-
- **Partial purge failure** — `delete_failures` is non-empty but at least one path was deleted: keep record-and-continue. Set `purge_status = "partial"` so step 3's on-disk result record reflects it (the `output-contract-schema.md` `status` enum supports `"partial"`); proceed to section 5. The headless single-line envelope has no `"partial"` value in its enum, so it stays `"success"` while `context_files_failed`/`verification_errors`/the report surface the unfreed paths.
|
|
228
|
-
- **No failures** — `delete_failures` is empty: set `purge_status = "success"` and proceed to section 5.
|
|
160
|
+
- **`failed`**: at least one path was attempted and none was deleted, so the purge accomplished none of its destructive intent and must NOT report success. HALT (exit code 4, `halt_reason: "delete-failed"`, phase `execute:delete`): "**Purge failed:** none of the target folders could be deleted: {list each `delete_failures` path with its error}. The manifest and context files were already updated in sections 2–3; the on-disk files remain and can be removed by hand (`rm -rf {path}`)." The §1 halt envelope carries the resolved `skill`, `drop_mode` and `versions_affected`, `files_deleted: []`, and `manifest_updated` from section 2. Do not proceed to section 5.
|
|
161
|
+
- **`partial`**: `delete_failures` is non-empty but at least one path was deleted. Keep record-and-continue: `purge_status = "partial"` lets step 3's on-disk result record reflect it (the `output-contract-schema.md` `status` enum supports `"partial"`). Record the warning `delete_failed: {path}: {error}` for each `delete_failures` entry, then proceed to section 5.
|
|
162
|
+
- **`success`**: nothing failed (a path already gone is not a failure). Proceed to section 5.
|
|
229
163
|
|
|
230
164
|
### 5. Verify Final State
|
|
231
165
|
|
|
232
166
|
Run these verification checks:
|
|
233
167
|
|
|
234
|
-
1. **Manifest check
|
|
235
|
-
|
|
236
|
-
|
|
168
|
+
1. **Manifest check** (skip it when `target_in_manifest == false`: section 2 changed no entry). Read the entry through `{manifestOpsHelper}`:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
python3 {manifestOpsHelper} {skills_output_folder} get {target_skill}
|
|
172
|
+
```
|
|
237
173
|
|
|
238
|
-
|
|
174
|
+
Version-level drop: `entry.versions.{version}.status` is `"deprecated"` for each version in `target_versions`. Skill-level deprecate: every version in `entry.versions` has `status` `"deprecated"`. Skill-level purge: `status` is `"not_found"`. A failure's manual fix: correct the `{target_skill}` entry of `{skills_output_folder}/.export-manifest.json` by hand.
|
|
239
175
|
|
|
240
|
-
|
|
176
|
+
2. **Context files check** (skip it when `context_files_updated` is empty): list the rows the rebuilt files still hold for the dropped skill, by name and version only:
|
|
241
177
|
|
|
242
|
-
|
|
178
|
+
```bash
|
|
179
|
+
uv run {dropRosterHelper} rows --skill {target_skill} [--version {version}] {each file in context_files_updated, quoted}
|
|
180
|
+
```
|
|
243
181
|
|
|
244
|
-
|
|
182
|
+
Pass `--version` for a version-level drop. Each `rows` entry fails the check, as does each `unchecked` entry (a file whose rows could not be read); the manual fix for both is to re-run `[EX] Export Skill`, which rebuilds the file. When the call exits non-zero, the check fails for every file in `context_files_updated`, with the helper's `error` (its stderr when stdout holds no JSON).
|
|
183
|
+
|
|
184
|
+
If any verification fails, record it in `verification_errors` with its manual fix, record the warning `verification_failed: {what failed}: {its manual fix}`, and do not halt: proceed to step 3 so the report shows what succeeded and what needs manual attention.
|
|
245
185
|
|
|
246
|
-
|
|
186
|
+
### 6. Store Results in Context
|
|
247
187
|
|
|
248
|
-
|
|
249
|
-
- `disk_freed` — human-readable size (purge mode) or `"N/A (soft drop)"`
|
|
250
|
-
- `delete_failures` — list of `{path, error}` for paths whose deletion was attempted but failed (empty if none; a *full* purge failure already HALTed in section 4 and never reaches this step)
|
|
251
|
-
- `purge_status` — `"success"`, `"partial"` (some paths failed to delete), or `"success"` for soft drops; step 3 maps this to the on-disk result record's `status` field
|
|
252
|
-
- `manifest_updated` — boolean (true if section 2 succeeded)
|
|
253
|
-
- `context_files_updated` — list of successfully rebuilt files
|
|
254
|
-
- `context_files_failed` — list of files that failed to rebuild (empty if none)
|
|
255
|
-
- `verification_errors` — list of verification failures (empty if none)
|
|
188
|
+
Step 3 reads what this step bound where it ran: `files_deleted`, `disk_freed`, `delete_failures`, `purge_status`, `manifest_updated`, `context_files_updated`, `context_files_failed` and `verification_errors`, with `forge_left_in_place` carried from step 1.
|
|
256
189
|
|
|
257
190
|
### 7. Load Next Step
|
|
258
191
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Invocation Contract: skf-drop-skill
|
|
2
|
+
|
|
3
|
+
The arguments, gates, exit codes and headless result envelope of `skf-drop-skill`. Interactive runs do not need this file; headless automators and pipelines read it, and the SKILL.md On-Activation HALTs follow its Halt Envelope section.
|
|
4
|
+
|
|
5
|
+
## Invocation Contract
|
|
6
|
+
|
|
7
|
+
| Aspect | Detail |
|
|
8
|
+
|--------|--------|
|
|
9
|
+
| **Inputs** | skill_name [required], mode (deprecate/purge) [required], version (`all` or one version) [required for a skill in the manifest; a draft takes only `all`]. An argument supplied at invocation answers its step 1 prompt in either mode, so that prompt is not shown |
|
|
10
|
+
| **Flags** | `--headless` / `-H` (auto-resolve all gates); `--dry-run` (run selection and display the §10 confirmation block, then exit with `status="dry-run"`: no manifest mutation, no file deletion) |
|
|
11
|
+
| **Gates** | step 1: Input Gate [use args] (§4 skill), Scope Gate [use args] (§6 version), Mode Gate [use args] (§8 mode), Confirm Gate [Y] |
|
|
12
|
+
| **Outputs** | Updated manifest, rebuilt context files, (purge: deleted directories), `drop-skill-result-{YYYYMMDD-HHmmss}.json` (UTC) and `drop-skill-result-latest.json` in `{skills_output_folder}` (none for a dry run or a HALT); a run folder under `{project-root}/_bmad-output/.skf-run/`, kept only after a HALT, except an interactive HALT in step 1 or 2, which deletes it |
|
|
13
|
+
| **Headless** | Gates auto-resolve with their default action (SKILL.md Workflow Rules); select.md §6 and §8 halt when the `version` or `mode` argument a headless run must pass is missing. Any non-empty `forbid_purge_in_headless`, set under `[workflow]` in a team or personal override (never in the bundled `customize.toml`, which every update overwrites), stops a headless `mode=purge` at On-Activation §4 (exit code 6, `halt_reason: "headless-purge-forbidden"`) when the customization resolver runs. When it cannot, including an override file that fails to parse, the run warns `customization_resolver_unavailable` and the bundled empty value leaves the guard off. A key written without the `[workflow]` line is ignored with no warning, and the guard stays off. |
|
|
14
|
+
| **Exit codes** | See "Exit Codes" below |
|
|
15
|
+
|
|
16
|
+
## Exit Codes
|
|
17
|
+
|
|
18
|
+
Every hard HALT exits with a stable code; the step files name the exact `halt_reason` and phase at each HALT site, and the envelope below carries both.
|
|
19
|
+
|
|
20
|
+
| Code | Meaning | Raised by (class) |
|
|
21
|
+
| ---- | -------------------- | ----------------- |
|
|
22
|
+
| 0 | success | step 4 (terminal) |
|
|
23
|
+
| 2 | input-missing / input-invalid | step 1 input gates: a missing or unmatched `skill_name`, `version` or `--mode` value (§4 / §6 / §8), or a draft skill given a specific `version` or `mode=deprecate` |
|
|
24
|
+
| 3 | resolution-failure | step 1 §2 corrupt manifest, §3 nothing to drop |
|
|
25
|
+
| 4 | write-failure | On-Activation §4, or step 1 §2 when the roster helper cannot run (`write-failed`); step 1 §9b, before any change (`context-rebuild-failed`); step 2 manifest write, in every mode (`manifest-write-failed`), or a purge that deleted nothing (`delete-failed`) |
|
|
26
|
+
| 5 | state-conflict | step 1 §7 active-version guard; §3, §4 or §8b ownership guard (`not-skf-output`) |
|
|
27
|
+
| 6 | user-cancelled | any interactive cancel or confirm-gate `[N]`; On-Activation headless-purge guard |
|
|
28
|
+
|
|
29
|
+
## Result Contract (Headless)
|
|
30
|
+
|
|
31
|
+
The single-line JSON contract every headless run of skf-drop-skill prints. The shared emitter, `shared/scripts/skf-emit-result-envelope.py`, builds each line from the payload the emitting step hands it and checks it against `shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json`, so no stage types an envelope and a HALT prints the right shape whatever else is still in context.
|
|
32
|
+
|
|
33
|
+
When `{headless_mode}` is true, step 3 prints this envelope on **stdout** before chaining to step 4, a `--dry-run` prints it on **stdout** at the step 1 confirmation gate, and every HARD HALT prints it on **stderr** with `status: "error"`:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
SKF_DROP_SKILL_RESULT_JSON: {"status":"success|error|dry-run","skill":"…|null","drop_mode":"…|null","versions_affected":[],"files_deleted":[],"would_delete":[],"forge_left_in_place":null,"manifest_updated":false,"result_path":null,"exit_code":0,"halt_reason":null,"error":null}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Field rules:
|
|
40
|
+
|
|
41
|
+
- `status`: `"success"` on the terminal happy path, `"dry-run"` when `--dry-run` was set and the workflow stopped at the confirmation gate before any change, `"error"` on any HALT.
|
|
42
|
+
- `halt_reason`: on success and dry-run it is `null`; otherwise one of `"input-missing"`, `"input-invalid"`, `"manifest-corrupt"`, `"nothing-to-drop"`, `"active-version-guard-refused"`, `"not-skf-output"`, `"headless-purge-forbidden"`, `"manifest-write-failed"`, `"context-rebuild-failed"`, `"delete-failed"`, `"write-failed"`, `"user-cancelled"`.
|
|
43
|
+
- `exit_code`: the emitter derives it from `halt_reason`, per the Exit Codes table above (`0` on success and dry-run).
|
|
44
|
+
- `skill`, `drop_mode`, `versions_affected`, `files_deleted`, `manifest_updated`: the values the emit site knows; a key not resolved yet takes the default shown in the template (`null`, `[]` or `false`). `versions_affected` is a list holding the one version dropped, or the string `"all"` for the whole skill.
|
|
45
|
+
- `would_delete`: in a dry run of a purge, the folders the purge would delete (the step 1 purge check's `affected_directories`), so an automator can check the blast radius before it runs the drop; `[]` otherwise, and in a dry run of a deprecate.
|
|
46
|
+
- `forge_left_in_place`: the forge folder a purge leaves in place (or, in a dry run, would leave) because SKF did not generate it; `null` otherwise.
|
|
47
|
+
- `result_path`: the per-run result file step 3 wrote, `{skills_output_folder}/drop-skill-result-{YYYYMMDD-HHmmss}.json` (UTC), stamped by the emitter; `null` for a dry run and a HALT, which write no result file.
|
|
48
|
+
- `error`: `null` on success and dry-run; on a HALT, `{"phase", "reason"}`, plus `path` when the halt names one: the step and gate that halted, and the halt message.
|
|
49
|
+
- `warnings`: present only when the run recorded at least one. A run that reports `success` can still carry the degraded outcomes step 2 records, which the report also shows: `context_rebuild_failed: <context file>: <error>` (a context file it could not rebuild), `active_link_dangling: <skill folder>/active: <manual fix>` (an `active` link a version purge left pointing at no version), `delete_failed: <path>: <error>` (a folder a partial purge could not delete) and `verification_failed: <what failed>: <manual fix>` (a final-state check that failed). Any run can carry `result_file_write_failed: <path>: <reason>` when the emitter could not write a result file, and `customization_resolver_unavailable: <reason>` when On Activation records that the customization resolver could not run, so the overrides under `{project-root}/_bmad/custom/` were not applied.
|
|
50
|
+
|
|
51
|
+
Each emit site supplies its `halt_reason`, `exit_code` and phase, and whichever discriminating fields it knows; the emitter fills in the rest.
|
|
52
|
+
|
|
53
|
+
## Halt Envelope
|
|
54
|
+
|
|
55
|
+
Every HALT names its exit code, `halt_reason` and phase where it stops. In headless mode it prints its envelope before it stops: it stages `{run_dir}/halt.json` as `{"phase": "<phase>", "reason": "<the halt message>", "halt_reason": "<halt_reason>", "exit_code": <code>}`, plus `"path"` when the halt names one and the envelope fields the site names, runs the first command below and displays the line it prints verbatim. The SKILL.md On-Activation run-folder halt has no folder to stage in, so it hands that payload, on one line, to the second command. Write the payload as valid JSON: in the halt message and `path`, replace each backslash with / and each double quote with a backtick. The emitter gives every field the payload leaves out its default (`null`, `[]` or `false`) and checks `exit_code` against `halt_reason`. When the helper is missing, exits non-zero or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing; in step 1 or 2 it then deletes the run folder.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
uv run {emitEnvelopeHelper} emit-halt --workflow skf-drop-skill --run-dir "{run_dir}" --target stderr < "{run_dir}/halt.json"
|
|
59
|
+
uv run {emitEnvelopeHelper} emit-halt --workflow skf-drop-skill --target stderr <<'SKF_DROP_HALT'
|
|
60
|
+
{"phase": "on-activation:run-folder", "reason": "<the halt message>", "halt_reason": "write-failed", "exit_code": 4}
|
|
61
|
+
SKF_DROP_HALT
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
SKILL.md On-Activation §4 binds `{emitEnvelopeHelper}` and `{run_dir}`, and its HALTs follow this section. select.md and execute.md state the same rule in their section 1, so the HALTs of a step never depend on this file.
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'health-check.md'
|
|
3
|
-
# §
|
|
4
|
-
#
|
|
5
|
-
# versions
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# the manifest in-prompt.
|
|
9
|
-
manifestOpsProbeOrder:
|
|
10
|
-
- '{project-root}/_bmad/skf/shared/scripts/skf-manifest-ops.py'
|
|
11
|
-
- '{project-root}/src/shared/scripts/skf-manifest-ops.py'
|
|
3
|
+
# SKILL.md On-Activation §4 binds `{manifestOpsHelper}`, which §1 reads the
|
|
4
|
+
# skill's remaining versions through (`get` for the versions and their status,
|
|
5
|
+
# `affected-versions` for the numeric semver-descending order), and
|
|
6
|
+
# `{emitEnvelopeHelper}` with `{run_dir}`: the emitter stamps the time and the
|
|
7
|
+
# result file names, so this step types neither.
|
|
12
8
|
---
|
|
13
9
|
|
|
14
10
|
<!-- Config: communicate in {communication_language}. Render the report block in {document_output_language}. -->
|
|
@@ -22,20 +18,19 @@ Present a clear, final summary of what the drop workflow changed — manifest st
|
|
|
22
18
|
## Rules
|
|
23
19
|
|
|
24
20
|
- Focus only on reporting results stored in context by step 2 — do not re-execute any part of the drop
|
|
25
|
-
- Do not hide verification errors
|
|
26
|
-
- Chains to the local health-check step via `{nextStepFile}` after completion (see §3)
|
|
21
|
+
- Do not hide verification errors, failed context file rebuilds or folders a purge could not delete
|
|
27
22
|
|
|
28
23
|
## MANDATORY SEQUENCE
|
|
29
24
|
|
|
30
25
|
### 1. Determine Remaining Versions
|
|
31
26
|
|
|
32
|
-
**If `is_skill_level == true`:**
|
|
27
|
+
**If `is_skill_level == true` and `drop_mode == "purge"`:**
|
|
33
28
|
|
|
34
29
|
Set `remaining_versions_display = "(skill fully removed)"`.
|
|
35
30
|
|
|
36
|
-
**
|
|
31
|
+
**Otherwise** (one version, or a whole-skill deprecate, which keeps every version in the manifest as `deprecated`):
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
Read the target skill's remaining versions through the `{manifestOpsHelper}` On-Activation §4 resolved, rather than re-parsing the manifest by hand:
|
|
39
34
|
|
|
40
35
|
```bash
|
|
41
36
|
python3 {manifestOpsHelper} {skills_output_folder} get {target_skill}
|
|
@@ -50,8 +45,6 @@ python3 {manifestOpsHelper} {skills_output_folder} affected-versions {target_ski
|
|
|
50
45
|
- 0.1.0 (deprecated)
|
|
51
46
|
```
|
|
52
47
|
|
|
53
|
-
**If neither `{manifestOpsProbeOrder}` candidate resolves:** read `exports.{target_skill}.versions` from `{skills_output_folder}/.export-manifest.json` in-prompt, list each remaining version with its `status` (active marked `*`), ordering newest-first by comparing version components numerically.
|
|
54
|
-
|
|
55
48
|
### 2. Render the Report
|
|
56
49
|
|
|
57
50
|
Display the following block, filling in values from context:
|
|
@@ -71,6 +64,8 @@ Changes:
|
|
|
71
64
|
{if drop_mode == "purge":}
|
|
72
65
|
- Files deleted: {list from files_deleted, or "(none — nothing on disk)"}
|
|
73
66
|
- Disk space freed: {disk_freed}
|
|
67
|
+
{if delete_failures is non-empty:}- Not deleted: {each delete_failures path with its error} (remove by hand: `rm -rf {path}`)
|
|
68
|
+
{if forge_left_in_place:}- Left in place (not SKF output): {forge_left_in_place}
|
|
74
69
|
|
|
75
70
|
Remaining versions for {target_skill}:
|
|
76
71
|
{remaining_versions_display}
|
|
@@ -84,40 +79,61 @@ restore the managed section entry.
|
|
|
84
79
|
{if verification_errors is non-empty:}
|
|
85
80
|
**Verification warnings:**
|
|
86
81
|
{list each verification error}
|
|
87
|
-
|
|
82
|
+
Each item above names its manual fix.
|
|
88
83
|
```
|
|
89
84
|
|
|
90
85
|
### Result Contract
|
|
91
86
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
`headless_provenance` persists the §8/§10 decision trail from step 1 so an unattended run's auto-decisions survive in the durable record, not only in the transient log — a consumer can tell an operator-confirmed drop from a headless auto-confirmed one without re-deriving it:
|
|
87
|
+
The shared emitter writes the result contract (`shared/references/output-contract-schema.md`) and prints the envelope; this step stages their content. Write `{run_dir}/result-context.json`:
|
|
95
88
|
|
|
96
89
|
```json
|
|
97
|
-
|
|
90
|
+
{
|
|
91
|
+
"status": "success",
|
|
92
|
+
"skill": "{target_skill}",
|
|
93
|
+
"drop_mode": "{drop_mode}",
|
|
94
|
+
"versions_affected": {target_versions},
|
|
95
|
+
"files_deleted": {files_deleted},
|
|
96
|
+
"forge_left_in_place": {forge_left_in_place},
|
|
97
|
+
"manifest_updated": {manifest_updated},
|
|
98
|
+
"result_contract": {
|
|
99
|
+
"skill": "skf-drop-skill",
|
|
100
|
+
"status": "{record_status}",
|
|
101
|
+
"outputs": [{"type": "skill", "path": "{each path in files_deleted}"}],
|
|
102
|
+
"summary": {
|
|
103
|
+
"target_skill": "{target_skill}",
|
|
104
|
+
"drop_mode": "{drop_mode}",
|
|
105
|
+
"versions_affected": {target_versions},
|
|
106
|
+
"forge_left_in_place": {forge_left_in_place},
|
|
107
|
+
"headless_provenance": {"headless": {headless_mode}, "mode_source": "{mode_source}", "confirm": "{confirm_source}"}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
98
111
|
```
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
- `{target_versions}` is a JSON array (e.g. `["0.5.0"]`) or the string `"all"`; `{files_deleted}` a JSON array of absolute paths (`[]` in deprecate mode, and so is `outputs`); `{forge_left_in_place}` the path step 2 carried, or `null` when none and in deprecate mode; `{manifest_updated}` the boolean from step 2.
|
|
114
|
+
- `{record_status}` is `"partial"` when some (but not all) purge folders failed to delete (step 2's `purge_status`); then also put step 2's `delete_failures` in `summary.delete_failures`. Otherwise it is `"success"`. A full purge failure and a failed manifest write never reach this step: step 2 HALTs with `halt_reason: "delete-failed"` or `"manifest-write-failed"`.
|
|
115
|
+
- `headless_provenance` persists the §8/§10 decision trail from step 1, so an unattended run's auto-decisions survive in the durable record and a consumer can tell an operator-confirmed drop from a headless auto-confirmed one: `{headless_mode}` is the resolved boolean, and `{mode_source}` and `{confirm_source}` the values step 1 set.
|
|
116
|
+
- The payload carries no `warnings`: the emitter adds the ones step 2 recorded, so a run whose envelope reads `success` still names what needs a manual fix.
|
|
103
117
|
|
|
104
|
-
|
|
118
|
+
Then run, in every mode:
|
|
105
119
|
|
|
106
|
-
```
|
|
107
|
-
|
|
120
|
+
```bash
|
|
121
|
+
uv run {emitEnvelopeHelper} emit --workflow skf-drop-skill --run-dir "{run_dir}" --result-dir "{skills_output_folder}" < "{run_dir}/result-context.json"
|
|
108
122
|
```
|
|
109
123
|
|
|
110
|
-
|
|
124
|
+
The emitter stamps the UTC time, the run id, the run's warnings and the auto-decisions select.md recorded (`headless_decisions`) into the record, writes it as `{skills_output_folder}/drop-skill-result-{YYYYMMDD-HHmmss}.json`, copies it to `{skills_output_folder}/drop-skill-result-latest.json` (the stable path pipelines read), and prints the `SKF_DROP_SKILL_RESULT_JSON:` line on stdout (field rules in `references/invocation-contract.md`). Bind `{result_json_path}` ← that line's `result_path`: null when the record could not be written, and the line's `warnings` then say why. When `{headless_mode}` is true, display the line verbatim before chaining to step 4. If the helper exits non-zero, correct `result-context.json` from the `message` of its stderr JSON and run it once more; if it fails again, display "The drop result record could not be written: {message}" and go on, since the drop itself is complete.
|
|
111
125
|
|
|
112
126
|
### Post-drop hook (optional)
|
|
113
127
|
|
|
114
|
-
If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`), invoke it once the
|
|
128
|
+
If `{onCompleteCommand}` is non-empty (resolved at SKILL.md On Activation §3 from `workflow.on_complete`) and `{result_json_path}` is not null, invoke it once the emitter has written the record:
|
|
115
129
|
|
|
116
130
|
```bash
|
|
117
131
|
{onCompleteCommand} --result-path={result_json_path}
|
|
118
132
|
```
|
|
119
133
|
|
|
120
|
-
|
|
134
|
+
When it exits non-zero, display "on_complete hook failed (exit {code}): {its first stderr line}" as a line of its own (after the envelope line when `{headless_mode}` is true), and never fail the workflow on a hook error: the drop has already completed and may be irreversible. When `{onCompleteCommand}` is empty (bundled default), skip the invocation entirely.
|
|
135
|
+
|
|
136
|
+
Whether or not a hook ran, then delete the run folder, whose payload the emitter has read: `rm -rf "{run_dir}"`.
|
|
121
137
|
|
|
122
138
|
### 3. Chain to Health Check
|
|
123
139
|
|