bmad-module-skill-forge 2.2.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 +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 +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 +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 +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 +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 +1 -1
- 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 +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 +15 -5
- 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 +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 +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 -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 +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 -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,6 +1,5 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 'report.md'
|
|
3
|
-
descriptionGuardProtocol: '{project-root}/src/shared/references/description-guard-protocol.md'
|
|
4
3
|
# Resolve `{descriptionGuardHelper}` by probing `{descriptionGuardProbeOrder}`
|
|
5
4
|
# in order (installed SKF module path first, src/ dev-checkout fallback);
|
|
6
5
|
# first existing path wins. HALT if neither resolves — letting an external
|
|
@@ -10,26 +9,34 @@ descriptionGuardProbeOrder:
|
|
|
10
9
|
- '{project-root}/_bmad/skf/shared/scripts/skf-description-guard.py'
|
|
11
10
|
- '{project-root}/src/shared/scripts/skf-description-guard.py'
|
|
12
11
|
# Resolve `{updateActiveSymlinkHelper}` to the first existing path; HALT if
|
|
13
|
-
# neither candidate exists. §
|
|
14
|
-
# symlink; §
|
|
15
|
-
# the helper,
|
|
12
|
+
# neither candidate exists. §8 uses it to atomically flip the active
|
|
13
|
+
# symlink; §8a uses it (verify mode) to confirm the post-state. Without
|
|
14
|
+
# the helper, an "rm and recreate" pattern would leave a brief window where
|
|
16
15
|
# concurrent readers see a missing symlink.
|
|
17
16
|
updateActiveSymlinkProbeOrder:
|
|
18
17
|
- '{project-root}/_bmad/skf/shared/scripts/skf-update-active-symlink.py'
|
|
19
18
|
- '{project-root}/src/shared/scripts/skf-update-active-symlink.py'
|
|
20
19
|
# Resolve `{verifyProvenanceCompletenessHelper}` to the first existing path.
|
|
21
|
-
# §
|
|
22
|
-
#
|
|
23
|
-
# set-diff (missing + orphaned entries) and file:line citation resolution.
|
|
24
|
-
# Advisory — do not HALT if neither path resolves; fall back to the in-prose
|
|
25
|
-
# set comparison §6a documents (graceful degradation).
|
|
20
|
+
# Advisory: if neither resolves, §2 and §6 look up no node kind and §6 runs
|
|
21
|
+
# its by-hand set comparison, since the files it checks are already written.
|
|
26
22
|
verifyProvenanceCompletenessProbeOrder:
|
|
27
23
|
- '{project-root}/_bmad/skf/shared/scripts/skf-verify-provenance-completeness.py'
|
|
28
24
|
- '{project-root}/src/shared/scripts/skf-verify-provenance-completeness.py'
|
|
25
|
+
# Resolve `{extractionPatternsData}` to the first existing path. If neither
|
|
26
|
+
# exists, §2 relabels nothing and §6 fixes no node kind: list each label
|
|
27
|
+
# violation and each node-kind finding as a WARN.
|
|
28
|
+
extractionPatternsDataProbeOrder:
|
|
29
|
+
- '{project-root}/_bmad/skf/skf-create-skill/references/extraction-patterns.md'
|
|
30
|
+
- '{project-root}/src/skf-create-skill/references/extraction-patterns.md'
|
|
31
|
+
# Resolve `{atomicWriteHelper}` to the first existing path. Advisory: if
|
|
32
|
+
# neither resolves, §6 fixes no node kind and lists each one as a WARN.
|
|
33
|
+
atomicWriteProbeOrder:
|
|
34
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py'
|
|
35
|
+
- '{project-root}/src/shared/scripts/skf-atomic-write.py'
|
|
29
36
|
# Resolve `{hashContentHelper}` to the first existing path; HALT if neither
|
|
30
|
-
# candidate exists. §1
|
|
31
|
-
#
|
|
32
|
-
#
|
|
37
|
+
# candidate exists. §1 and §7 use its `manual-verify` subcommand to verify
|
|
38
|
+
# SKILL.md against the byte-exact [MANUAL] inventory step 4 §4 amended; a
|
|
39
|
+
# marker-count comparison would pass a block whose interior was truncated
|
|
33
40
|
# without changing the marker count.
|
|
34
41
|
hashContentProbeOrder:
|
|
35
42
|
- '{project-root}/_bmad/skf/shared/scripts/skf-hash-content.py'
|
|
@@ -42,65 +49,157 @@ hashContentProbeOrder:
|
|
|
42
49
|
renderMetadataStatsProbeOrder:
|
|
43
50
|
- '{project-root}/_bmad/skf/shared/scripts/skf-render-metadata-stats.py'
|
|
44
51
|
- '{project-root}/src/shared/scripts/skf-render-metadata-stats.py'
|
|
52
|
+
# Resolve `{sourceTreeHelper}` to the first existing path. §9 uses its
|
|
53
|
+
# `advance` subcommand to move the workspace clone to the commit §2
|
|
54
|
+
# recorded. If neither path exists, skip the call and continue: it never
|
|
55
|
+
# gates the workflow.
|
|
56
|
+
sourceTreeProbeOrder:
|
|
57
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-source-tree.py'
|
|
58
|
+
- '{project-root}/src/shared/scripts/skf-source-tree.py'
|
|
59
|
+
# Resolve `{cccGitHygieneHelper}` to the first existing path. It keeps ccc's
|
|
60
|
+
# index folders and SKF's workspace lock out of git, and undoes the
|
|
61
|
+
# `.gitignore` edit `ccc init` makes in a workspace clone. If neither path
|
|
62
|
+
# exists, skip the call and continue: it never gates the workflow.
|
|
63
|
+
cccGitHygieneProbeOrder:
|
|
64
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-ccc-git-hygiene.py'
|
|
65
|
+
- '{project-root}/src/shared/scripts/skf-ccc-git-hygiene.py'
|
|
66
|
+
# Resolve `{checkWorkspaceDriftHelper}` to the first existing path. §9
|
|
67
|
+
# passes it to the advance, which moves the workspace clone only when the
|
|
68
|
+
# helper confirms the clone still holds the skill's previous commit. If
|
|
69
|
+
# neither path exists, run the advance without `--drift-helper`: it then
|
|
70
|
+
# leaves an existing clone where it is (`head-unverified`), and §9 warns.
|
|
71
|
+
checkWorkspaceDriftProbeOrder:
|
|
72
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-check-workspace-drift.py'
|
|
73
|
+
- '{project-root}/src/shared/scripts/skf-check-workspace-drift.py'
|
|
74
|
+
# Resolve `{buildChangeManifestHelper}` to the first existing path; HALT if
|
|
75
|
+
# neither exists: §3's `apply` writes the provenance map from this run's
|
|
76
|
+
# records, never by hand.
|
|
77
|
+
buildChangeManifestProbeOrder:
|
|
78
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-build-change-manifest.py'
|
|
79
|
+
- '{project-root}/src/shared/scripts/skf-build-change-manifest.py'
|
|
80
|
+
# Resolve `{compareDocHashesHelper}` to the first existing path when step 2
|
|
81
|
+
# wrote `{run_dir}/doc-hashes.json` (a docs-only skill); HALT if neither
|
|
82
|
+
# exists: §2 records the changed documents' new hashes with its
|
|
83
|
+
# `refresh-hashes`, so the next update does not find them changed again.
|
|
84
|
+
compareDocHashesProbeOrder:
|
|
85
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-docs.py'
|
|
86
|
+
- '{project-root}/src/shared/scripts/skf-detect-docs.py'
|
|
87
|
+
# Resolve `{validateOutputHelper}` to the first existing path when §7 finds no
|
|
88
|
+
# skill-check. If neither exists, §7 records the spec check as skipped.
|
|
89
|
+
validateOutputProbeOrder:
|
|
90
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-output.py'
|
|
91
|
+
- '{project-root}/src/shared/scripts/skf-validate-output.py'
|
|
92
|
+
# `{extractPublicApiHelper}`: §2's public API counts; if neither exists, §2
|
|
93
|
+
# counts by hand.
|
|
94
|
+
extractPublicApiProbeOrder:
|
|
95
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
|
|
96
|
+
- '{project-root}/src/shared/scripts/skf-extract-public-api.py'
|
|
97
|
+
entryPointsByHandData: 'skf-create-skill/references/entry-points-by-hand.md'
|
|
45
98
|
---
|
|
46
99
|
|
|
47
100
|
<!-- Config: communicate in {communication_language}. -->
|
|
48
101
|
|
|
49
|
-
# Step
|
|
102
|
+
# Step 5: Write Updated Files
|
|
50
103
|
|
|
51
104
|
## STEP GOAL:
|
|
52
105
|
|
|
53
|
-
Verify the merged SKILL.md that step 4 section 6b wrote to disk,
|
|
106
|
+
Verify the merged SKILL.md that step 4 section 6b wrote to disk, write the derived artifacts (metadata.json, provenance-map.json, evidence-report.md, context-snippet.md), check the written package, and only then point the active symlink at the new version.
|
|
54
107
|
|
|
55
108
|
## Rules
|
|
56
109
|
|
|
57
110
|
- Focus only on verifying merged files and writing derived artifacts — merge content was already written in step 4
|
|
58
|
-
- Do not modify merged SKILL.md content
|
|
111
|
+
- Do not modify merged SKILL.md or `references/` content, apart from §6's citation line and prefix fixes outside `[MANUAL]` blocks, and §7's `skill-check check --fix` edits and body split, which moves no section that holds a `[MANUAL]` block; §7 re-checks the [MANUAL] blocks after them. Any mismatch detected during verification triggers HALT, not repair
|
|
59
112
|
- Do not skip provenance map update — critical for future audits
|
|
60
|
-
- HALT immediately on verification failure before
|
|
113
|
+
- HALT immediately on verification failure: §1 halts before any derived artifact is written, and §8 points the `active` link at the new version only after §6 and §7, the last checks that can halt, so every halt in this step rolls back a version no reader sees yet. A partial-write skill package is worse than an unchanged one
|
|
61
114
|
|
|
62
115
|
## Steps
|
|
63
116
|
|
|
117
|
+
**Halt procedure.** Every HALT in this step names its payload (`status`, `phase`, `path` when it has one, and `reason`), displays its message, and then runs, from `{project-root}`, the halt helper SKILL.md On Activation resolved.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv run {runStateHelper} halt --run-dir "{run_dir}" \
|
|
121
|
+
[--tree "{source_tree}"] \
|
|
122
|
+
[--lock "{forge_data_folder}/{skill_name}/.skf-update.lock" --owner "{lock_owner}"] \
|
|
123
|
+
[--emit] <<'SKF_JSON'
|
|
124
|
+
{"status": "<status>", "phase": "<phase>", "path": "<path; leave the key out when the halt names none>", "reason": "<reason>", "skill_name": "{skill_name}", "version": "<the metadata.json version the run started from>", "previous_version": "<the same>", "update_mode": "<normal, gap-driven or degraded>", "files_written": [<what stands: none after a rollback; the artifacts this step wrote when the helper kept a live version>]}
|
|
125
|
+
SKF_JSON
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Pass `--tree` when init.md §6b bound `{source_tree}`, always `--lock` and `--owner`, and `--emit` in `{headless_mode}`. Until §10 closes the window, the helper first undoes what this run wrote: in gap-driven mode it restores the package, the version's provenance map and evidence report, and the skill brief from the snapshot step 4 §6b took; in every other mode it removes the version folders step 4 created, unless the `active` link already names them (§8), which keeps that version, now the skill's live one (its `kept`). Then it removes the private source tree, releases the run lock (never one another run holds) and, with `--emit`, prints the halt's `SKF_UPDATE_RESULT_JSON:` line through the shared emitter, which adds the decisions recorded so far, the `error` object and a warning for each step the helper could not finish (`rollback-incomplete`, `source-tree-not-removed`, `run-lock-not-released`); it never stops on a result. Tell the user in one line what it restored, removed or kept, and name each path its `failed[]` lists for the user to restore or delete by hand. Write each payload value as a JSON string: escape `"` and `\`, and write every path with `/`. Display the line it prints verbatim. When it exits 1 and its message names the payload, fix the payload once and run it again, which redoes nothing already done; when it still fails or prints no line, display the halt message alone. An interactive HALT displays its message and emits nothing. A HALT that names no payload (a helper the frontmatter says to HALT without, resolving to no path) takes `status: "blocked"`, `phase: "write:<the helper's file name>"` and `reason: "<the helper's file name> is missing; re-install SKF"`.
|
|
129
|
+
|
|
130
|
+
The halt leaves `{run_dir}` in place.
|
|
131
|
+
|
|
132
|
+
A `Write` or `Edit` call in §2 to §5 that errors (permission denied, disk full, path invalid) HALTs with status `halted-for-write-failure`: the halt procedure takes `phase: "write:artifact-write"`, `path: "{the file that failed}"`, `reason: "<the error>"`. §6 checks what `metadata.json` and `provenance-map.json` hold.
|
|
133
|
+
|
|
134
|
+
**Warnings go to the run log.** Record each warning this step adds to `warnings[]` the moment it is raised: write its text to `{run_dir}/warning.txt` with a file write (a warning can hold quotes, `$` or backticks), then, from `{project-root}`, run `uv run {emitEnvelopeHelper} record --run-dir "{run_dir}" --warning "$(cat "{run_dir}/warning.txt")"`. The halt line and the result line read warnings only from `{run_dir}/warnings.jsonl`.
|
|
135
|
+
|
|
64
136
|
### 0. Description Guard Protocol
|
|
65
137
|
|
|
66
|
-
**Used by:** §7 (`skill-check check --fix` and `skill-check split-body --write`)
|
|
138
|
+
**Used by:** §7 (`skill-check check --fix` and `skill-check split-body --write`).
|
|
139
|
+
|
|
140
|
+
The deterministic phases are executed via `{descriptionGuardHelper}`: §7 invokes the helper at the capture and verify-restore points around every `skill-check` call.
|
|
67
141
|
|
|
68
|
-
|
|
142
|
+
**Guard outputs.** Bind `{guarded_description}` ← `description` from each `capture`, run while the in-context SKILL.md copy matches the file on disk. Bind `{guard_restored}` ← `restored` and `{guard_diff_kind}` ← `diff_kind` from each `verify-restore`. When `{guard_restored}` is true, set the in-context `description` to `{guarded_description}` so later sections do not work from the tool-mutated value, and record `description_guard_restored: true` with the tool name and `description_guard_diff_kind: {guard_diff_kind}` in workflow context for the evidence report (§4). A later `verify-restore` that exits 0 with `{guard_restored}` false leaves those records in place.
|
|
69
143
|
|
|
70
|
-
|
|
144
|
+
**Empty-snapshot rule.** `verify-restore` refuses an empty or whitespace-only `--captured-description` (exit 1, file untouched). Never re-run it with the empty value: writing it back would blank the field the guard protects. If the merged description is still in context (the in-context SKILL.md copy), re-run `verify-restore` with that value. Otherwise record `description_guard_restored: false` and `description_guard_refused: empty-capture` with the tool name; the evidence report (§4) renders that as a fired guard, not as a clean run.
|
|
71
145
|
|
|
72
146
|
### 1. Verify SKILL.md Write
|
|
73
147
|
|
|
74
148
|
SKILL.md was written in step 4 section 6b. Verify the write landed intact before proceeding to any derived-artifact writes.
|
|
75
149
|
|
|
76
|
-
- Verify the resolved `{skill_package}` path matches the version directory step 4 wrote to (
|
|
77
|
-
- Run the deterministic [MANUAL]-integrity verifier against the byte-exact inventory
|
|
150
|
+
- Verify the resolved `{skill_package}` path matches the version directory step 4 wrote to (outside gap-driven mode, step 4 §6b created `{skill_group}/{new_version}/` and rebound `{skill_package}` and `{forge_version}` to the new version)
|
|
151
|
+
- Run the deterministic [MANUAL]-integrity verifier against the byte-exact inventory step 4 §4 amended with the user's [MANUAL] decisions (the step 1 §5 inventory, when there were none):
|
|
78
152
|
|
|
79
153
|
```bash
|
|
80
|
-
uv run {hashContentHelper} manual-verify {skill_package}/SKILL.md \
|
|
81
|
-
--inventory {manual_inventory}
|
|
154
|
+
uv run {hashContentHelper} manual-verify "{skill_package}/SKILL.md" \
|
|
155
|
+
--inventory "{manual_inventory}"
|
|
82
156
|
```
|
|
83
157
|
|
|
84
158
|
The verdict JSON is `{"preserved":[...], "modified":[...], "missing":[...], "moved":[...], "ok":bool}`. A `modified` block is one whose byte-exact interior changed (an interior truncation); a `missing` block lost its markers entirely; a `moved` block is byte-identical but relocated with its logical parent section (clean — does not fail the gate). `ok == (modified empty AND missing empty)`.
|
|
85
|
-
- If `ok == true` and the path resolves: proceed to section 2
|
|
86
|
-
- **If `ok == false`: HALT immediately** with status `halted-for-manual-mismatch`. Do not write `metadata.json`, `provenance-map.json`, or any other artifact
|
|
159
|
+
- If `ok == true` and the path resolves: proceed to section 2. Step 4 §6b checked the same file against the same inventory before it published it, so this is the confirmation that the write landed
|
|
160
|
+
- **If `ok == false`: HALT immediately** with status `halted-for-manual-mismatch`. Do not write `metadata.json`, `provenance-map.json`, or any other artifact: further writes would compound the inconsistency. The halt procedure takes `phase: "write:verify-manual-integrity"`, `path: "{skill_package}/SKILL.md"`, `reason: "[MANUAL] blocks differ from the approved inventory: modified {modified}; missing {missing}"`. Alert the user:
|
|
161
|
+
|
|
162
|
+
"**[MANUAL] section integrity failure after write.** Blocks modified (interior changed): {modified}. Blocks missing (markers lost): {missing}. Relocated-but-intact (advisory only): {moved}. Verified against the inventory `{manual_inventory}`, on disk at `{skill_package}/SKILL.md`. {manual_recovery}"
|
|
163
|
+
|
|
164
|
+
`{manual_recovery}` depends on the mode:
|
|
87
165
|
|
|
88
|
-
|
|
166
|
+
- **Outside gap-driven mode** (step 4 §6b created a new version folder): "The previous version at `{skill_group}/{baseline_version}/` is unchanged, and the halt removed `{skill_group}/{new_version}/` and `{forge_data_folder}/{skill_name}/{new_version}/`, which step 4 created for this update: re-run update-skill."
|
|
167
|
+
- **Gap-driven mode** (the repair edited the current version in place): "The halt restored the package, with its [MANUAL] blocks, its provenance map and evidence report, and the skill brief, from the snapshot step 4 took before its first write: the skill is as it was. Re-run update-skill."
|
|
89
168
|
|
|
90
169
|
### 2. Write Updated metadata.json
|
|
91
170
|
|
|
171
|
+
**Renew the run lock first:** step 4 §8 may have waited at its gate since step 4 §6b renewed it. From `{project-root}`, run the renewal step 4 §6b ran:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
uv run {runLockHelper} acquire \
|
|
175
|
+
--lock "{forge_data_folder}/{skill_name}/.skf-update.lock" \
|
|
176
|
+
--owner "{lock_owner}" \
|
|
177
|
+
--stale-after 60
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
- **Exit 0 with `refreshed` true:** continue.
|
|
181
|
+
- **Exit 0 with `refreshed` false, or exit 3:** this run's lock lapsed while it waited, so another update may have changed the skill since this run read it (on exit 3 that update holds the lock). HALT with status `halted-for-concurrent-run` before writing `metadata.json`: display "**This update of {skill_name} lost its run lock while it waited.** {lock_recovery}", with the helper's `message` on exit 3. The halt procedure takes `phase: "write:run-lock"`, `path: "{forge_data_folder}/{skill_name}/.skf-update.lock"`, `reason: "run-lock-lost: this run's lock lapsed while it waited; another update may have changed the skill since this run read it"`, or on exit 3 `reason: "another update in progress: {message}"`.
|
|
182
|
+
- **Any other exit, or no JSON:** HALT with status `blocked` the same way: display "**The run lock could not be renewed:** {the message the helper printed on stderr}. {lock_recovery}", with `reason: "run-lock-failed: {that message}"`.
|
|
183
|
+
|
|
184
|
+
`{lock_recovery}` is, outside gap-driven mode, "The previous version is unchanged, and the halt removed `{skill_group}/{new_version}/` and `{forge_data_folder}/{skill_name}/{new_version}/`, which step 4 created for this update: re-run update-skill once no other update of {skill_name} runs." and, in gap-driven mode, "This repair already wrote SKILL.md in place; the halt restored the package and the skill brief from the snapshot step 4 took, so the skill is as it was: re-run update-skill once no other update of {skill_name} runs."
|
|
185
|
+
|
|
92
186
|
Update `{skill_package}/metadata.json`:
|
|
93
187
|
- **First, apply any queued `metadata_patches[]`** (staged by merge Priority 8b from gap-driven `metadata update` entries): apply each surgical patch described in the gap's remediation (reconcile a divergent count, add an explanatory stat, etc.) *before* the automatic recount below, so the recount overrides only the fields it owns and the patch survives for any field it does not. If a patch and the recount disagree on a field the recount owns (e.g., `exports_documented`), the recount wins — log the divergence so a still-stale stat surfaces in the report rather than being silently overwritten.
|
|
94
|
-
- **For gap-driven rescopes** (`DELETED_EXPORT` / verification `rescoped`): the removed exports are already dropped from the `exports` array below, and `stats` recompute from that reduced surface
|
|
95
|
-
- Update `version
|
|
96
|
-
- Update `generation_date` timestamp to current
|
|
188
|
+
- **For gap-driven rescopes** (`DELETED_EXPORT` / verification `rescoped`): the removed exports are already dropped from the `exports` array below, and `stats` recompute from that reduced surface: never set a `stats` count by hand to match the documented total. The reduction is justified by the `brief.scope.exclude` + `scope.amendments[]` (`action: "excluded"`) step 4 §6b wrote from gap-driven.md §1's rule R1; the recount simply reflects the smaller surface.
|
|
189
|
+
- Update `version` to `{new_version}`, the version step 4 §6b chose: **if `update_mode == "gap-driven"`, that is the unchanged version**: the skill is being repaired against the same source commit, so of the fields that mark an update only `generation_date` below changes, and step 4 wrote into the existing version directory (step 1 §6c records no source version in gap-driven mode). Otherwise it is the version whose folder step 4 §6b created: `{source_version_detected}` when step 1 §6c recorded `source_version_detected`, else the next patch version. Never pick another value here: `version` must name the folder `metadata.json` sits in, the one §8 points the `active` link at. From here on `{version}` is `{new_version}`.
|
|
190
|
+
- Update `generation_date` timestamp to the current UTC time to the second, `YYYY-MM-DDTHH:MM:SSZ`, read from the clock (`date -u +%Y-%m-%dT%H:%M:%SZ`), never typed: step 1 §4b offers a test report only when it is newer. It is the only update bookkeeping metadata.json carries, in every mode: §3's update operation block records `last_update` and `update_type` in provenance-map.json. Write neither key into metadata.json, and remove a `last_update` or `update_type` an older SKF version left there, so metadata.json never keeps the date or type of an earlier update beside the map's current one.
|
|
191
|
+
- **Record the source commit** (when `{source_tree_status}` is `ready` or `offline`): set `source_commit` to `{target_commit}`, the commit every step of this run read, and, when `{target_ref_override}` is set, `source_ref` to `{target_ref}`. Leave `source_root` as metadata.json records it: it names the clone SKF keeps for the repository; never write `{source_tree}` or any other path of this run into an artifact. For any other source (a local folder, gap-driven mode, docs-only) leave `source_commit` and `source_ref` unchanged. The in-context `{source_commit}` stays the value init.md §6 read; §9 needs it.
|
|
97
192
|
- Update `exports` array to reflect current export list
|
|
98
|
-
- **Compute the `stats` block and `confidence_distribution` deterministically** with `{renderMetadataStatsHelper}` (resolve from `{renderMetadataStatsProbeOrder}`; first existing path wins)
|
|
193
|
+
- **Compute the `stats` block and `confidence_distribution` deterministically** with `{renderMetadataStatsHelper}` (resolve from `{renderMetadataStatsProbeOrder}`; first existing path wins): the same helper sibling create-skill compiles them with, so create and update emit byte-identical stats for identical inputs. The helper owns all the arithmetic: it bins each provenance `entries[]` row once by its `signature_source` tier into `confidence_distribution.{t1,t1_low,t2,t3}`, sets `exports_documented` = the entry count for the library shape (for a reference app, the Pattern Surface row count; see **Shape** below), and derives `exports_total` = `exports_public_api` + `exports_internal`, `public_api_coverage` = documented / public_api (`null` if public_api is 0), `total_coverage` = documented / total (`null` if total is 0), plus `scripts_count` / `assets_count` from the inventory arrays. Run `uv run {renderMetadataStatsHelper} --help` for the full contract. Do not hand-bin the distribution: binning T2 annotations + T3 doc items on top of the per-export tiers double-counts, which per-entry binning by `signature_source` makes structurally impossible. You supply only the payload:
|
|
99
194
|
|
|
100
|
-
**
|
|
101
|
-
- `exports_public_api
|
|
102
|
-
- `exports_internal`: count of all other non-underscore-prefixed exports
|
|
195
|
+
**Payload (passed as JSON on stdin):**
|
|
196
|
+
- `exports_public_api` and `exports_internal`, unless the drift override below applies. At Quick tier, count the entry points by hand with `{entryPointsByHandData}`, as create-skill does. Otherwise, from `{project-root}`, run `uv run {extractPublicApiHelper} --mode full --source-root "{source_root}" [--brief "{forge_data_folder}/{skill_name}/skill-brief.yaml"] [--language "{language}"] [--scope-type "{scope_type}"] --head-cap 0 -o "{run_dir}/public-api.json"` once over the whole scope (no `--files-from`), with the longest timeout your shell tool takes (a large tree takes minutes), as create-skill's extract.md §4 does, and pass its `counts.exports_public_api` and `counts.exports_internal`, as its compile.md §4 does. Pass `--brief` when that file exists (step 4 §6b wrote a rescope's amendment into it), and `--language` and `--scope-type` when metadata.json records them. On exit 1 (`incomplete`) run it once more. When its `files_without_recipes` is not empty (in-scope files no recipe reads), read those files' entry points as `{entryPointsByHandData}` says and add their names to the two counts, as create-skill's extract.md §4b does. Still incomplete, or on exit 2 or 3, no JSON, no candidate, or `entry_points.status` `no-entry-point`, count the entry points by hand with `{entryPointsByHandData}`. A docs-only skill has no tree to count: pass the values metadata.json records, as the drift override does.
|
|
103
197
|
- `scripts` / `assets`: the scripts / assets inventory arrays (or `[]` when empty) — the helper sets `scripts_count` / `assets_count` from their lengths
|
|
198
|
+
- `pattern_surfaces_documented` (reference app only): the number of rows in the `## Pattern Surface` table of the merged SKILL.md, the count create-skill's compile.md §4 passes. The helper uses it as `exports_documented` and emits it as `stats.pattern_surfaces_documented`, the count test-skill measures a reference app's coverage by.
|
|
199
|
+
|
|
200
|
+
**Public API counts under the drift override** (`{workspace_drift_status}` is `overridden`, gap-driven.md §3): count nothing at `{source_root}`, which is HEAD, not the pinned commit. Pass as `exports_public_api` and `exports_internal` the values `{skill_package}/metadata.json` records in `stats` once the queued `metadata_patches[]` above are applied (0 for one it does not record, with a WARN).
|
|
201
|
+
|
|
202
|
+
**Shape:** read `scope_type` from `{skill_package}/metadata.json` before this section rewrites it (create-skill records the brief's `scope.type` there). When it is `reference-app`, pass `--shape reference-app` and put `pattern_surfaces_documented` in the payload, as create-skill's compile.md §4 does; for any other `scope_type`, or none, pass no `--shape` (the helper's default, the library shape).
|
|
104
203
|
|
|
105
204
|
**Invoke** — since the helper reads `entries[]`, stage §3's `provenance-map.json` write first (§3 does not depend on these stats):
|
|
106
205
|
|
|
@@ -109,57 +208,68 @@ Update `{skill_package}/metadata.json`:
|
|
|
109
208
|
| uv run {renderMetadataStatsHelper} {forge_version}/provenance-map.json
|
|
110
209
|
```
|
|
111
210
|
|
|
112
|
-
|
|
211
|
+
For a reference app, the same call with the Pattern Surface row count and the shape:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
echo '{"exports_public_api": {N}, "exports_internal": {M}, "pattern_surfaces_documented": {P}, "scripts": {scripts-inventory-or-[]}, "assets": {assets-inventory-or-[]}}' \
|
|
215
|
+
| uv run {renderMetadataStatsHelper} {forge_version}/provenance-map.json --shape reference-app
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Write the returned `stats` and `confidence_distribution` objects into `metadata.json` **verbatim**. If the helper reports `coherence.ok: false`, fix the provenance map and re-run the helper; never hand-edit the stats. A `confidence_distribution` violation means some provenance entries carry a missing or unrecognized `signature_source` (§3 must write it on every entry).
|
|
219
|
+
|
|
220
|
+
**Label violations.** A violation whose `field` is `provenance.entries[<i>].<field>` means entry `<i>` (its `export_name` is in the violation) carries a label its `extraction_method` does not allow: relabel it by the `## Relabel Rule` section of `{extractionPatternsData}`, with `{source_root}` as the tree its node-kind lookup reads. **Under the drift override** (`{workspace_drift_status}` is `overridden`, gap-driven.md §3) run no recipe at `{source_root}`, so no `kind-at`: HEAD is not the pinned commit, so a kind read there says nothing about the entry. Leave each violation that needs a kind from the tree in place and list it as a WARN ending ` (drift override: HEAD {head_short_sha} is not pinned {pinned_short_sha})`; a relabel that reads nothing from the tree still applies. With the status `ok` or `skipped`, run the lookup as the rule says. Rewrite `{forge_version}/provenance-map.json` with the relabeled entries, re-run the helper with the same payload and `--shape`, and write the `stats` and `confidence_distribution` it returns: a relabeled `signature_source` moves the distribution, and a violation left as a WARN does not block the write. This relabel applies to every entry the helper flags, including a gap-driven `verified` entry (§3). Record the relabeled export names, and each WARN left, as `{provenance_relabels}` in workflow context: the update report lists them under its Confidence Tier Breakdown (report.md), so no entry moves from T1 to T1-low unannounced.
|
|
221
|
+
|
|
222
|
+
**A docs-only skill's document hashes.** When step 2 wrote `{run_dir}/doc-hashes.json` (its docs-only branch), record the new hash of each document it found changed, after the writes above, so the next update compares against the documents this one read. Resolve `{compareDocHashesHelper}` ← first existing path in `{compareDocHashesProbeOrder}` and, from `{project-root}`, run:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
uv run {compareDocHashesHelper} refresh-hashes "{skill_package}/metadata.json" --compare "{run_dir}/doc-hashes.json"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
It sets the `content_hash` and `recorded_at` of each `doc_sources[]` entry whose URL is in the comparison's `changed[]`, keeps every other entry and field, and rewrites `metadata.json` through one rename: never edit `doc_sources` by hand. On exit 2, no JSON, or no candidate resolves: HALT with status `halted-for-write-failure` (halt procedure: `phase: "write:doc-sources"`, `path: "{skill_package}/metadata.json"`, `reason: "<its error, or skf-detect-docs.py is missing; re-install SKF>"`).
|
|
113
229
|
|
|
114
230
|
### 3. Write Updated provenance-map.json
|
|
115
231
|
|
|
116
|
-
Write
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
**For new exports:**
|
|
143
|
-
- Add new entry with full structured fields: `export_name`, `export_type`, `params[]`, `return_type`, `source_file`, `source_line`, `confidence`, `extraction_method`, `ast_node_type`
|
|
144
|
-
|
|
145
|
-
**For script/asset file changes (if `file_entries` exists):**
|
|
146
|
-
- MODIFIED_FILE: copy updated file to `scripts/` or `assets/`, update `content_hash` in `file_entries`
|
|
147
|
-
- DELETED_FILE: remove file from `scripts/` or `assets/`, remove entry from `file_entries`
|
|
148
|
-
- NEW_FILE: copy file to `scripts/` or `assets/`, add entry to `file_entries` with `file_name`, `file_type`, `source_file`, `confidence: "T1-low"`, `extraction_method: "file-copy"`, `content_hash`
|
|
149
|
-
|
|
150
|
-
**Add update operation metadata:**
|
|
151
|
-
```json
|
|
152
|
-
{
|
|
153
|
-
"last_update": "{current_date}",
|
|
154
|
-
"update_type": "{incremental if normal mode | full if degraded_mode}",
|
|
155
|
-
"files_changed": {count},
|
|
156
|
-
"exports_affected": {count},
|
|
157
|
-
"confidence_tier": "{tier}",
|
|
158
|
-
"manual_sections_preserved": {count}
|
|
159
|
-
}
|
|
232
|
+
Write `{forge_version}/provenance-map.json` with `{buildChangeManifestHelper}` `apply` (resolve it ← first existing path in `{buildChangeManifestProbeOrder}`), never by hand: it reads the map the update started from and this run's records in `{run_dir}`, and writes the whole updated map through a temporary file and one rename. When step 4 §6b created a new version, `{forge_version}` is its forge folder, which holds a copy of the previous map when there was one: write the whole updated map there, and leave the previous version's map as it was. From `{project-root}`, run:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
uv run {buildChangeManifestHelper} apply \
|
|
236
|
+
--update-type "{update_type}" \
|
|
237
|
+
[--provenance-map "{forge_version}/provenance-map.json"] \
|
|
238
|
+
[--manifest "{run_dir}/change-manifest.json"] \
|
|
239
|
+
[--category-c "{run_dir}/category-c.json"] \
|
|
240
|
+
[--ccc-pairs "{run_dir}/ccc-pairs.json"] \
|
|
241
|
+
[--extraction "{run_dir}/extraction.json"] \
|
|
242
|
+
[--export-details "{run_dir}/export-details.json"] \
|
|
243
|
+
[--reextract-records "{run_dir}/reextract-records.json"] \
|
|
244
|
+
[--merge-records "{run_dir}/merge-records.json"] \
|
|
245
|
+
[--file-compare "{run_dir}/category-d-compare.json"] \
|
|
246
|
+
[--new-files "{run_dir}/new-files.json"] \
|
|
247
|
+
[--promoted-docs "{run_dir}/promoted-docs.json"] \
|
|
248
|
+
[--source-root "{source_root}"] \
|
|
249
|
+
--skill-name "{skill_name}" \
|
|
250
|
+
--generation-date "{generation_date}" \
|
|
251
|
+
[--test-report-run-id "{test_report_run_id}"] \
|
|
252
|
+
--confidence-tier "{forge_tier}" \
|
|
253
|
+
--manual-sections-preserved "{manual_sections_preserved}" \
|
|
254
|
+
--source-commit "{map_source_commit}" \
|
|
255
|
+
--source-ref "{map_source_ref}" \
|
|
256
|
+
[--drift-head "{head_short_sha}" --drift-pinned "{pinned_short_sha}"] \
|
|
257
|
+
-o "{forge_version}/provenance-map.json"
|
|
160
258
|
```
|
|
161
259
|
|
|
162
|
-
`manual_sections_preserved`
|
|
260
|
+
`{update_type}` follows the run's mode as report.md §2 names it (`{update_mode}`): `incremental` for `normal`, `gap-driven` for `gap-driven`, `full` for `degraded` (a degraded run has no map: leave out `--provenance-map`, and `apply` starts a new one). Pass each `{run_dir}` file the run wrote: the change manifest and the records in every mode, `category-c.json` and `ccc-pairs.json` (step 2's Category C and its CCC check), `extraction.json` and `export-details.json` when step 2 wrote them, `merge-records.json` when step 4 did, the Category D files (`category-d-compare.json`, `new-files.json`) with `--source-root` when step 2 ran Category D, and `promoted-docs.json` when step 2 §1b wrote it. `{generation_date}` is the value §2 wrote, `--test-report-run-id` goes only with a gap-driven run (the `{test_report_run_id}` of the report it applied), `{manual_sections_preserved}` is `len(preserved) + len(moved)` from the §1 `manual-verify` verdict (never a marker count), and `{map_source_commit}` and `{map_source_ref}` are the `source_commit` and `source_ref` §2 writes to `metadata.json` (an empty string for a null one): audit-skill reads its baseline commit from the map's top-level `source_commit` and `source_ref`. Pass `--drift-head` and `--drift-pinned` only when `{workspace_drift_status}` is `overridden` (gap-driven.md §3). Every entry `apply` writes carries `signature_source`, which §2's stats helper bins, and `apply` sets the map's update operation block (`"last_update"`, `"update_type"` and its other keys), replacing an earlier update's.
|
|
261
|
+
|
|
262
|
+
Dispatch on its exit code:
|
|
263
|
+
|
|
264
|
+
- **0:** it printed `{status: "written", entries, file_entries, warnings, map}`. Bind `{provenance_spot_check_warnings}` ← its `warnings`, and add each to `warnings[]`; the update report lists each as a WARN (report.md §2).
|
|
265
|
+
- **3** (`status` `refused`): a blocking gap reached the provenance write with no source line (`blocking_unresolved[]`: a `NEW_EXPORT` the map does not hold, `unknown`, with a severity other than `Medium`, `Low` or `Info`, a missing one included). gap-driven.md §4 sends every such gap to its §4a and halts with `halted-for-remediation-path` before merge when §4a does not find it, so this happens only when that check was skipped or bypassed: `apply` wrote nothing, and no null citation is written. HALT with status `blocked` and write no `metadata.json`, `provenance-map.json` or other artifact. Tell the user: "**A blocking gap reached the provenance write with no source line:** {each export name, with its severity or "no severity"}. gap-driven.md §4 should have stopped on it with `halted-for-remediation-path`. The halt restored SKILL.md and references/ from the snapshot step 4 took; give each gap a `file:line` Source or name the file that defines its export in its Remediation (or downgrade it to Medium, Low or Info), then re-run update-skill." The halt procedure takes `phase: "write:provenance-map"`, `path: "{forge_version}/provenance-map.json"`, `reason: "blocking-gap-unresolved: {export names}: gap-driven.md §4 should have halted with halted-for-remediation-path"`.
|
|
266
|
+
- **Exit 1, no JSON, or no candidate resolves:** HALT with status `halted-for-write-failure` (halt procedure: `phase: "write:provenance-map"`, `path: "{forge_version}/provenance-map.json"`, `reason: "<its stderr, or skf-build-change-manifest.py is missing; re-install SKF>"`).
|
|
267
|
+
|
|
268
|
+
**For script/asset file changes:** `apply` updates the rows; copy or remove the files themselves in `{skill_package}`, each path the change manifest's `category_d` lists: a `scripts_*` path under `scripts/`, an `assets_*` path under `assets/`:
|
|
269
|
+
- MODIFIED_FILE (`*_modified`): copy the file from `{source_root}` (the row's `content_hash` is updated by `apply`)
|
|
270
|
+
- DELETED_FILE (`*_deleted`): remove the file
|
|
271
|
+
- NEW_FILE (`*_added`): copy the file from `{source_root}` (`apply` adds its row with `file_name`, `file_type`, `source_file`, `confidence: "T1-low"`, `extraction_method: "file-copy"`, `content_hash`)
|
|
272
|
+
- A `docs_*` path is source-tracked, never bundled: copy and remove nothing for it.
|
|
163
273
|
|
|
164
274
|
### 4. Write Updated evidence-report.md
|
|
165
275
|
|
|
@@ -171,6 +281,7 @@ Append update operation section to `{forge_version}/evidence-report.md` (create
|
|
|
171
281
|
**Trigger:** {manual / audit-skill chain}
|
|
172
282
|
**Forge Tier:** {tier}
|
|
173
283
|
**Mode:** {normal / degraded}
|
|
284
|
+
**Source commit:** {source_commit_line}
|
|
174
285
|
|
|
175
286
|
### Changes Detected
|
|
176
287
|
- Files modified: {count}
|
|
@@ -185,11 +296,19 @@ Append update operation section to `{forge_version}/evidence-report.md` (create
|
|
|
185
296
|
- [MANUAL] sections preserved: {count}
|
|
186
297
|
- Conflicts resolved: {count}
|
|
187
298
|
|
|
299
|
+
### Scope and Targeted Re-Extraction
|
|
300
|
+
- Authoritative files: {authoritative_files_mirror, or none}
|
|
301
|
+
- Scope reconciliation before detection: {scope_reconciliation_pre, or none}
|
|
302
|
+
- Scope reconciliation after detection: {scope_reconciliation_post, or none}
|
|
303
|
+
- Targeted re-extraction: {targeted_reextraction, or none}
|
|
304
|
+
|
|
188
305
|
### Validation Summary
|
|
189
|
-
- Spec compliance: {PASS/WARN/FAIL}
|
|
190
|
-
- [MANUAL] integrity: {PASS/
|
|
191
|
-
- Confidence tiers: {PASS/WARN
|
|
192
|
-
- Provenance: {PASS/WARN
|
|
306
|
+
- Spec compliance: {PASS/WARN/FAIL/SKIP} (quality score {score}/100)
|
|
307
|
+
- [MANUAL] integrity: {PASS/FAIL}
|
|
308
|
+
- Confidence tiers: {PASS/WARN}
|
|
309
|
+
- Provenance: {PASS/WARN}
|
|
310
|
+
- Diff: {new} new, {fixed} fixed issues, or SKIP
|
|
311
|
+
- Security: {PASS/WARN/SKIP}
|
|
193
312
|
|
|
194
313
|
### Description Guard
|
|
195
314
|
- Restored: {true/false}
|
|
@@ -203,7 +322,11 @@ Append update operation section to `{forge_version}/evidence-report.md` (create
|
|
|
203
322
|
- Notes: {one-sentence detail or —}
|
|
204
323
|
```
|
|
205
324
|
|
|
206
|
-
**
|
|
325
|
+
**Scope and Targeted Re-Extraction population:** read `{run_dir}/evidence-records.jsonl`, where step 2 (§1b, §1c and §2.2) and gap-driven.md §4 appended their records, one JSON line each. Write each record under its key as it was recorded, and `none` for a key no line holds (or when the file does not exist), so a headless decision such as a deletion-ratio continue stays visible after the run.
|
|
326
|
+
|
|
327
|
+
**Validation Summary population:** §4 writes `[MANUAL] integrity` from §1's `manual-verify` verdict (`PASS` when `ok`) and `Confidence tiers` from §2 (`PASS` when the stats helper reported no label violation, else `WARN` naming `{provenance_relabels}`), with placeholders for the rest. §6 fills `Provenance`, and §7 fills `Spec compliance`, `Diff` and `Security`, each in the on-disk report, as §5 fills the Context Snippet sub-block.
|
|
328
|
+
|
|
329
|
+
**Description Guard population** (used by §7 Post-Write Validation when the §0 protocol fires): fill all four fields from context when `description_guard_restored == true` (triggering tool, whether restore succeeded, and what changed, based on the recorded `description_guard_diff_kind`). When `Restored: false`, the other three fields are `—` — this is the clean-run expected state — except when `description_guard_refused == "empty-capture"` (§0's empty-snapshot rule): then set `Triggering tool` to the recorded tool name, `Original description preserved: false`, and `Notes: guard refused — empty captured snapshot (empty-capture)`, so a refused restore is distinguishable from a run where the guard never fired. Same field semantics and populator logic as create-skill step 6 §8.
|
|
207
330
|
|
|
208
331
|
**Context Snippet population** (used by §5 after the staleness check runs): §4 writes the sub-block with placeholders; §5 updates the on-disk evidence report in place after deciding whether to regenerate. Set `Regenerated: true` and populate `Triggers fired` with any combination of `headline-exports`, `version`, `gotchas` when at least one trigger fired. Set `Regenerated: false` and `Triggers fired: —` when none fired (the gap-driven / internals-only outcome). Always fill `Notes` with a one-sentence reason (e.g., `"Gap-driven repair — no snippet surface changed"`, `"Version bumped 0.1.0 → 0.2.0; headline exports re-ranked"`).
|
|
209
332
|
|
|
@@ -221,7 +344,7 @@ Append update operation section to `{forge_version}/evidence-report.md` (create
|
|
|
221
344
|
|
|
222
345
|
**Record the decision on the on-disk evidence report:** open `{forge_version}/evidence-report.md` (written by §4 with placeholder values in the `### Context Snippet` sub-block) and update that sub-block under the Update Operation section just written. Set `Regenerated: true|false`, fill `Triggers fired:` with the list of triggers that fired (or `—` when none), and write a one-sentence `Notes:` entry. See §4's "Context Snippet population" note for field semantics.
|
|
223
346
|
|
|
224
|
-
**If no trigger fired:** skip regeneration
|
|
347
|
+
**If no trigger fired:** skip regeneration: do not touch `context-snippet.md` on disk. The snippet remains valid against the prior run's surface. Continue to §6.
|
|
225
348
|
|
|
226
349
|
**If at least one trigger fired:** regenerate the snippet using the format from `skf-create-skill/assets/skill-sections.md` (pipe-delimited indexed format).
|
|
227
350
|
|
|
@@ -231,108 +354,167 @@ Pull values for the regenerated snippet from the updated metadata.json (version,
|
|
|
231
354
|
|
|
232
355
|
Write the regenerated snippet to `{skill_package}/context-snippet.md`, preserving file permissions.
|
|
233
356
|
|
|
234
|
-
###
|
|
357
|
+
### 6. Provenance Completeness
|
|
235
358
|
|
|
236
|
-
|
|
359
|
+
Provenance completeness is a deterministic set-diff plus citation resolution over `metadata.json` (§2) and `provenance-map.json` (§3). Run it against the just-written artifacts via `{verifyProvenanceCompletenessHelper}`:
|
|
237
360
|
|
|
238
361
|
```bash
|
|
239
|
-
uv run {
|
|
240
|
-
--
|
|
241
|
-
--
|
|
362
|
+
uv run {verifyProvenanceCompletenessHelper} verify \
|
|
363
|
+
--metadata "{skill_package}/metadata.json" \
|
|
364
|
+
--provenance "{forge_version}/provenance-map.json" \
|
|
365
|
+
--source-root "{source_root}" \
|
|
366
|
+
--skill-dir "{skill_package}" \
|
|
367
|
+
--check-node-kinds \
|
|
368
|
+
-o "{forge_data_folder}/{skill_name}/.skf-update-verify.json"
|
|
242
369
|
```
|
|
243
370
|
|
|
244
|
-
The helper
|
|
371
|
+
The helper reads its `--source-root` (falling back to the provenance map's own `source_root` field when the flag is omitted); pass the resolved `{source_root}` so citation resolution runs against the same tree re-extraction read. When `{source_root}` is null, empty or a URL (a docs-only skill), leave out the `--source-root` line: an empty flag value fails the helper's arguments, and the citation check must still run. When `{source_tree_status}` is `ready` or `offline`, `{source_root}` is still the tree init.md §6b prepared (step 7 removes it), so citations resolve against the commit §2 recorded. It writes its JSON to `{forge_data_folder}/{skill_name}/.skf-update-verify.json`, beside the §1b lock (each run overwrites it). **Read that JSON and do NOT recompute the set operations by eye, since an LLM set-diff can silently pass a dropped or orphaned entry:**
|
|
245
372
|
|
|
246
|
-
|
|
373
|
+
- `missing[]` — documented exports (metadata `exports[]`) with no provenance entry: a coverage gap.
|
|
374
|
+
- `orphaned[]` — provenance `entries[].export_name` whose export was removed but the entry remains.
|
|
375
|
+
- `summary.set_diff`: `not-applicable` for a reference app (`scope_type: reference-app` in `{skill_package}/metadata.json`), whose `exports[]` is empty by design while its entries follow each citation: the helper then diffs no sets, `missing[]` and `orphaned[]` stay empty, and its `status` comes from `stale[]`, `citations[]` and `node_kinds[]` alone. `checked` for every other skill.
|
|
376
|
+
- `stale[]`: entries whose `source_file:source_line` no longer resolves or is not the line that defines the export; each carries a `reason` of `file-missing`, `line-out-of-bounds`, `line-invalid` or `line-not-definition`. A `line-not-definition` item lists in `definition_lines` every line of the file that defines the export (the `def` or declaration line itself, not a decorator or blank line above it). An empty list means the rules found no definition line, which may be a shape they do not cover: the entry is unverified, not gone. Internal names are canonicalized through `reexport_map` before the diff, so a barrel-renamed export does not read as missing or orphaned.
|
|
377
|
+
- `citations[]`: `[AST:]` / `[SRC:]` citations in SKILL.md and `references/` whose prefix disagrees with the entry they cite (`prefix-mismatch`: `ast-grep` gives `[AST:]`, `source-read` gives `[SRC:]`), and `[AST:]` citations in a skill whose map has no `ast-grep` entry (`ast-without-ast-grep`). Each gives the markdown `file` and `line`, the `citation` text and its `expected_prefix`.
|
|
378
|
+
- `summary.stale_check` — `"checked"` when citations were resolved against the source tree, or `"skipped-no-source-root"` when no source root resolved on disk (the completeness + orphan diffs still ran; `stale` is empty by construction, not clean-by-verification).
|
|
379
|
+
- `summary.skill_citations_scanned` and `summary.skill_citations_matched`: when citations were scanned and none matched, no citation names a path and line the map records (the citations use another root), so the prefix check compared nothing. List that as a WARN.
|
|
380
|
+
- `node_kinds[]`: `ast-grep` entries whose `ast_node_type` is not shaped like a kind or that the ast-grep CLI does not know as a node kind of the entry's language (`invalid-kind`), or whose kind is `ERROR` (`error-kind`). Each gives the entry's `entry_index`, `export_name`, `source_file`, `source_line`, `ast_node_type` and `language`. `summary.node_kind_check` is `skipped-no-ast-grep` when no ast-grep CLI is on PATH, or `skipped-unrecognized-ast-grep` when the ast-grep found does not reject a kind no grammar has: either way ast-grep judged no kind. `node_kinds_unchecked[]` lists, with a `reason`, the entries whose kind ast-grep could not judge, and `summary.node_kind_check_skipped` counts the ast-grep entries in files ast-grep has no language for: list each unchecked entry, and the skipped count, as a WARN.
|
|
247
381
|
|
|
248
|
-
|
|
249
|
-
- **`flipped`** (exit 0): symlink was atomically updated (temp-and-replace). Continue to §6.
|
|
250
|
-
- **`missing-target`** (exit 2): `{skill_group}/{version}/` directory does not exist on disk. HALT — display `halt_message` verbatim. This indicates §4 §6b did not write the version directory before §5b ran (a workflow bug, not a user error).
|
|
251
|
-
- **`mismatch`** (exit 2): re-read after flip showed the symlink still points elsewhere. HALT — display `halt_message`. Should be impossible because the helper uses `os.replace` (atomic rename); a mismatch here indicates filesystem-level interference (concurrent writer, broken FUSE mount).
|
|
382
|
+
When the helper exits 2 it writes no JSON: record `Provenance: WARN (not run: verifier error)` with its stderr in the evidence report's Validation Summary and continue to §7. When the second run below exits 2, record the fixes already applied and `second run: verifier error` the same way and continue to §7.
|
|
252
383
|
|
|
253
|
-
|
|
384
|
+
**Fix the findings that have one answer**, in this order, then run the command above once more:
|
|
254
385
|
|
|
255
|
-
|
|
386
|
+
1. **Citation prefixes and source lines.** Never apply them by hand: from `{project-root}`, run the verifier's `fix` on the JSON the run above wrote:
|
|
256
387
|
|
|
257
|
-
|
|
388
|
+
```bash
|
|
389
|
+
uv run {verifyProvenanceCompletenessHelper} fix \
|
|
390
|
+
--verify "{forge_data_folder}/{skill_name}/.skf-update-verify.json" \
|
|
391
|
+
--provenance "{forge_version}/provenance-map.json" \
|
|
392
|
+
--skill-dir "{skill_package}" \
|
|
393
|
+
--manual-inventory "{manual_inventory}" \
|
|
394
|
+
[--no-line-moves]
|
|
395
|
+
```
|
|
258
396
|
|
|
259
|
-
|
|
260
|
-
-
|
|
261
|
-
-
|
|
262
|
-
-
|
|
397
|
+
Pass `--no-line-moves` when `{workspace_drift_status}` is `overridden` (gap-driven.md §3): the tree read is not the recorded commit, so its lines prove nothing about the map. `fix` prints `applied[]` (each fix it made), `left_as_warn[]` (each finding it left for a person, with its `why`), `files_written[]` and `manual_verify` (the SKILL.md [MANUAL] blocks checked against `{manual_inventory}` after its writes).
|
|
398
|
+
- **`manual_verify.ok` is false** (a [MANUAL] block changed): HALT with status `halted-for-manual-mismatch`. The halt procedure takes `phase: "write:verify-manual-integrity"`, `path: "{skill_package}/SKILL.md"`, `reason: "[MANUAL] blocks changed after the provenance fixes: ..."`, as §1's does. Alert the user: "**[MANUAL] section integrity failure after the provenance fixes.** Blocks modified (interior changed): {modified}. Blocks missing (markers lost): {missing}. Verified against the inventory `{manual_inventory}`. {manual_recovery}", with `{manual_recovery}` as §1 gives it: §8 has not pointed the `active` link at a new version yet, so this halt rolls back as §1's does.
|
|
399
|
+
- **Exit 2** (no JSON; its stderr line names any file it already wrote): record `Provenance: WARN (not fixed: verifier error)` with that line, re-read each file it names into context, and go on to step 2.
|
|
400
|
+
- **Otherwise:** re-read each file in `files_written[]` into the in-context copies (the merged SKILL.md and `references/` content and §3's provenance map), so later sections and the report read the fixed text, and add `SKILL.md` and `provenance-map.json` to `files_written[]` (report.md §5b) when `fix` wrote them.
|
|
401
|
+
2. **Node kinds.** For each `node_kinds[]` item, take the entry at its `entry_index` in `{forge_version}/provenance-map.json` as step 1 left it (its `export_name` and `source_file` confirm it). When this run's extraction record (a `files` record of `{run_dir}/reextract-records.json`, which re-extract.md §4 or gap-driven.md §4 wrote) names the recipe that matched that export (`ast_recipe`), set the entry's `ast_node_type` to the `kind` that recipe declares in `{extractionPatternsData}` (the ast-grep Patterns table in create-skill's `extraction-patterns-by-hand.md`, which that section of `{extractionPatternsData}` points to, gives the kind of a `find_code` pattern). When no record names one (a `verified` or `moved` entry has none), look the kind up at the entry's `source_file` and `source_line`: from `{project-root}`, run `uv run {verifyProvenanceCompletenessHelper} kind-at --source-root "{source_root}" --file "{source_file}" --line {source_line} --name "{export_name}" --recipes "{extractionPatternsData}"` and set the `kind` it prints when `status` is `found`. List a WARN, with the status, for any other status or an exit 2, when there is no local source tree, or when `{workspace_drift_status}` is `overridden` (gap-driven.md §3: the tree read is not the recorded commit, so run no `kind-at`). Never invent a kind, and never change `extraction_method` to clear the finding. Write the map with `python3 {atomicWriteHelper} write --target {forge_version}/provenance-map.json` (resolve it ← first existing path in `{atomicWriteProbeOrder}`, the new map on stdin); if neither path resolves, fix no node kind and list each one as a WARN. Apply the same change to §3's in-context map, and add `provenance-map.json` to `files_written[]`.
|
|
263
402
|
|
|
264
|
-
|
|
403
|
+
These fixes apply to any entry, a gap-driven `verified` or `moved` entry included (§3). Fix nothing else: each `left_as_warn[]` item stays for a person to decide, and so do `missing` / `orphaned` exports. A `line-not-definition` item with an empty `definition_lines` is never fixed: list its export as unverified.
|
|
265
404
|
|
|
266
|
-
"**
|
|
405
|
+
Map the re-run's `status` to the `Provenance:` line of the §4 evidence report's Validation Summary: `PASS` when `status == "pass"`, `WARN` when `status == "findings"`. For a reference app that status comes from its stale lines, citation prefixes and node kinds alone (`summary.set_diff` `not-applicable`): note `set diff not applicable: reference app` in the Validation Summary, never as a WARN. Provenance findings are **advisory**: they do not block the update. In the evidence report's Validation Summary, list the source lines, citation prefixes and node kinds this section fixed (`fix`'s `applied[]` and step 2's kinds) and the unverified exports, then each `left_as_warn[]` item and each other `missing` / `orphaned` / `stale` / `citations` / `node_kinds` finding the re-run still reports as a WARN so the user can decide, and note `source lines not checked: no local source tree` when `stale_check` was `skipped-no-source-root`, `node kinds not checked: no ast-grep CLI` or `node kinds not checked: unrecognized ast-grep` when `node_kind_check` was `skipped-no-ast-grep` or `skipped-unrecognized-ast-grep`, each `node_kinds_unchecked[]` entry with its `reason`, and `summary.node_kind_check_skipped` when it is above 0. Add each of those WARNs and the unverified exports to `warnings[]` with a `provenance:` prefix (for example `provenance: search line 26 does not define it; definition lines 27, 31`); §3 recorded `{provenance_spot_check_warnings}` already.
|
|
267
406
|
|
|
268
|
-
|
|
269
|
-
|------|--------|
|
|
270
|
-
| SKILL.md | {VERIFIED in section 1} |
|
|
271
|
-
| metadata.json | {VERIFIED/FAILED} |
|
|
272
|
-
| provenance-map.json | {VERIFIED/FAILED} |
|
|
273
|
-
| evidence-report.md | {VERIFIED/FAILED} |
|
|
274
|
-
| context-snippet.md | {VERIFIED/FAILED} |
|
|
275
|
-
| {skill_group}/active symlink | {VERIFIED/FAILED} (readlink → {resolved_version}, expected {version}) |
|
|
407
|
+
**Under the drift override** (`{workspace_drift_status}` is `overridden`, gap-driven.md §3), the verifier read `{source_root}` at HEAD, not the pinned commit, and a file or line missing at HEAD may still be at the pinned commit: end the `Provenance:` line of the Validation Summary, and each WARN this section lists for a `stale[]` finding (`file-missing`, `line-out-of-bounds`, `line-invalid`, `line-not-definition`), an unverified export or a finding `fix` left as `line-moves-skipped`, with ` (drift override: HEAD {head_short_sha} is not pinned {pinned_short_sha})`.
|
|
276
408
|
|
|
277
|
-
**
|
|
409
|
+
**Graceful degradation:** if neither probe path resolves (no `uv` / script available), fall back to the manual set comparison the script encapsulates: enumerate metadata `exports[]` and provenance `entries[].export_name` (canonicalizing internal names through `reexport_map`), diff the two sets for missing/orphaned entries, and spot-check that each `source_file:source_line` still points at a real line in the source tree. Skip the set diff for a reference app (`scope_type: reference-app` in `{skill_package}/metadata.json`), as the helper does: its `exports[]` is empty by design while its entries follow each citation, so spot-check the lines only and note `set diff not applicable: reference app`. The definition-line, citation-prefix and node-kind checks do not run in this fallback, and nothing is fixed: record `line, citation and node-kind checks not run: verifier missing` in the Validation Summary. Prefer the script: it does this deterministically.
|
|
278
410
|
|
|
279
|
-
|
|
411
|
+
### 7. Run Post-Write Validation
|
|
280
412
|
|
|
281
|
-
|
|
413
|
+
skill-check runs once per update, here, against the written package. Check that it is available, never assume it: run `npx skill-check -h`.
|
|
282
414
|
|
|
283
|
-
|
|
415
|
+
**Description Guard Protocol:** every invocation below that may modify SKILL.md (`skill-check check --fix` and any `split-body` write) must run inside the four-phase guard defined in §0. Invoke `{descriptionGuardHelper}` at the capture and verify-restore points around each call:
|
|
284
416
|
|
|
285
417
|
```bash
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
418
|
+
# Phase 1: capture before any frontmatter-touching tool call
|
|
419
|
+
uv run {descriptionGuardHelper} capture "{skill_package}/SKILL.md"
|
|
420
|
+
# stash returned `description` as `guarded_description`
|
|
421
|
+
|
|
422
|
+
# Phase 2: run the tool (skill-check --fix, split-body --write)
|
|
423
|
+
|
|
424
|
+
# Phases 3+4: verify and restore after the tool call
|
|
425
|
+
uv run {descriptionGuardHelper} verify-restore "{skill_package}/SKILL.md" \
|
|
426
|
+
--captured-description "{guarded_description}"
|
|
290
427
|
```
|
|
291
428
|
|
|
292
|
-
|
|
429
|
+
Do not rely on per-call ad-hoc preservation logic: use the helper.
|
|
293
430
|
|
|
294
|
-
-
|
|
295
|
-
- `orphaned[]` — provenance `entries[].export_name` whose export was removed but the entry remains.
|
|
296
|
-
- `stale[]` — entries whose `source_file:source_line` no longer resolves; each carries a `reason` of `file-missing`, `line-out-of-bounds`, or `line-invalid`. Internal names are canonicalized through `reexport_map` before the diff, so a barrel-renamed export does not read as missing or orphaned.
|
|
297
|
-
- `summary.stale_check` — `"checked"` when citations were resolved against the source tree, or `"skipped-no-source-root"` when no source root resolved on disk (the completeness + orphan diffs still ran; `stale` is empty by construction, not clean-by-verification).
|
|
431
|
+
**If skill-check is available:**
|
|
298
432
|
|
|
299
|
-
|
|
433
|
+
- Run `npx skill-check check "{skill_package}" --fix --format json` **inside the §0 guard**. One call validates the package against the spec, scores it, fixes what has one answer and runs the security scan (on unless `--no-security-scan` is passed). Read the JSON, not the exit code: the quality score (`scores[].score`, the entry matching the skill, or a top-level `qualityScore` on an older skill-check), the remaining `diagnostics[]`, `fixed[]` and the security findings. When the scan could not run (no `SNYK_TOKEN`), record `Security: SKIP (SNYK_TOKEN not configured)`.
|
|
434
|
+
- **Context sync after --fix:** if `fixed[]` is non-empty, re-read the SKILL.md `--fix` changed into the in-context copy, so the report does not work from a copy that differs from the file on disk. The §0 guard has already restored `description` if it diverged.
|
|
435
|
+
- If `body.max_lines` is reported, prefer a selective split: extract only the largest Tier 2 section(s) to `references/`, keeping Tier 1 inline (inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval). **Never move a section that holds a `[MANUAL]` block:** init.md §5 inventories SKILL.md only, so a block moved to `references/` would leave the next update's inventory. Fall back to `npx skill-check split-body "{skill_package}" --write`, **inside the §0 guard** (it can also touch frontmatter), only when SKILL.md holds no `[MANUAL]` block (§1's verdict lists none): it moves every section. Verify anchors resolve after a split.
|
|
436
|
+
- When a previous version is on disk (outside gap-driven mode), run `npx skill-check diff "{skill_group}/{baseline_version}/{skill_name}" "{skill_package}"` and record its new and fixed issues as informational; otherwise record `Diff: SKIP`.
|
|
300
437
|
|
|
301
|
-
**
|
|
438
|
+
**Re-check the [MANUAL] blocks** once `--fix` and any split have run, from `{project-root}`:
|
|
302
439
|
|
|
303
|
-
|
|
440
|
+
```bash
|
|
441
|
+
uv run {hashContentHelper} manual-verify "{skill_package}/SKILL.md" \
|
|
442
|
+
--inventory "{manual_inventory}"
|
|
443
|
+
```
|
|
304
444
|
|
|
305
|
-
|
|
445
|
+
On `ok` false, HALT with status `halted-for-manual-mismatch`, as §1 does. The halt procedure takes `phase: "write:verify-manual-integrity"`, `path: "{skill_package}/SKILL.md"`, `reason: "[MANUAL] blocks changed after skill-check: modified {modified}; missing {missing}"`. Alert the user: "**[MANUAL] section integrity failure after skill-check.** Blocks modified (interior changed): {modified}. Blocks missing (markers lost): {missing}. Verified against the inventory `{manual_inventory}`. {manual_recovery}", with `{manual_recovery}` as §1 gives it: §8 has not pointed the `active` link at a new version yet, so this halt rolls back as §1's does.
|
|
306
446
|
|
|
307
|
-
**
|
|
447
|
+
**If skill-check is unavailable:** record `Diff: SKIP` and `Security: SKIP (skill-check unavailable)`, and check the structure by script instead: resolve `{validateOutputHelper}` ← first existing path in `{validateOutputProbeOrder}` and, from `{project-root}`, run `uv run {validateOutputHelper} "{skill_package}"`. Record `Spec compliance: PASS (skf-validate-output.py)` when it reports no issue, and `Spec compliance: WARN (skf-validate-output.py)` with each issue it reports otherwise; when no candidate resolves, record `Spec compliance: SKIP (skill-check unavailable, no validator)`. Nothing here changes SKILL.md, so the [MANUAL] re-check above does not run.
|
|
448
|
+
|
|
449
|
+
Record the results in the evidence report's Validation Summary (§4): `Spec compliance` from the score and the remaining `diagnostics[]`, `Diff` and `Security`, with any `description_guard_restored` event the §0 protocol recorded. They are advisory: do not block on warnings.
|
|
450
|
+
|
|
451
|
+
### 8. Update the Active Symlink
|
|
452
|
+
|
|
453
|
+
Flip `{skill_group}/active` to point at the current `{version}` via the helper, now that §6 and §7 have checked the written package. The call is **always** run: atomic, idempotent, and verified in one shot. The helper's no-op path handles the "version did not change" case (gap-driven mode, or no source drift) without writing to disk:
|
|
308
454
|
|
|
309
455
|
```bash
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
456
|
+
uv run {updateActiveSymlinkHelper} update \
|
|
457
|
+
--skill-group {skill_group} \
|
|
458
|
+
--version {version}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
The helper emits a result envelope with `status` ∈ `{ok, flipped, mismatch, missing-target}` and a pre-formatted `log_message`. Log the message to the evidence report.
|
|
313
462
|
|
|
314
|
-
|
|
463
|
+
**Dispatch on `status`:**
|
|
315
464
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
465
|
+
- **`ok`** (exit 0): symlink already points at `{version}`, so no disk write. Continue to §8a.
|
|
466
|
+
- **`flipped`** (exit 0): symlink was atomically updated (temp-and-replace). Continue to §8a.
|
|
467
|
+
- **`missing-target`** (exit 2): `{skill_group}/{version}/` does not exist on disk, so step 4 §6b did not create it. HALT with status `halted-for-write-failure` and display `halt_message` verbatim.
|
|
468
|
+
- **`mismatch`** (exit 2): the link still points elsewhere after the flip. HALT with status `halted-for-write-failure` and display `halt_message` verbatim.
|
|
469
|
+
|
|
470
|
+
For both exit-2 halts, the halt procedure takes `phase: "write:active-symlink"`, `path: "{skill_group}/active"`, `reason: "<status>: <halt_message>"`. Its rollback removes the new version (the `active` link does not name it) or, in gap-driven mode, restores the package from the snapshot.
|
|
471
|
+
|
|
472
|
+
### 8a. Verify the Active Symlink
|
|
473
|
+
|
|
474
|
+
Check, read-only, that the `active` link resolves to the version §2 wrote to `metadata.json`, in every mode (consumers that do not read the export manifest fall back to the link, `knowledge/version-paths.md` §Reading Workflows step 5):
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
uv run {updateActiveSymlinkHelper} verify \
|
|
478
|
+
--skill-group {skill_group} \
|
|
479
|
+
--version {version}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
On `mismatch` (exit 2), HALT with status `halted-for-write-failure` and display the helper's `halt_message` verbatim: it names the diverged target, the expected version and the recovery command. The halt procedure takes `phase: "write:verify-active-symlink"`, `path: "{skill_group}/active"`, `reason: "mismatch: <halt_message>"`.
|
|
483
|
+
|
|
484
|
+
### 9. Move the Workspace Clone to the Recorded Commit
|
|
485
|
+
|
|
486
|
+
Run only when `{source_tree_status}` is `ready` and `{workspace_clone}` is not null. test-skill and audit-skill read the skill's source from metadata.json `source_root`, the clone SKF keeps for this repository, and test-skill compares its HEAD with the `source_commit` §2 just wrote. From `{project-root}`, resolve `{sourceTreeHelper}` ← first existing path in `{sourceTreeProbeOrder}`, `{cccGitHygieneHelper}` ← first existing path in `{cccGitHygieneProbeOrder}` and `{checkWorkspaceDriftHelper}` ← first existing path in `{checkWorkspaceDriftProbeOrder}`, then run:
|
|
487
|
+
|
|
488
|
+
```bash
|
|
489
|
+
uv run {sourceTreeHelper} advance \
|
|
490
|
+
--clone "{workspace_clone}" \
|
|
491
|
+
--source-repo "{source_repo}" \
|
|
492
|
+
--expect-commit "{source_commit}" \
|
|
493
|
+
--target "{target_commit}" \
|
|
494
|
+
--target-ref "{target_ref}" \
|
|
495
|
+
--tree "{source_tree}" \
|
|
496
|
+
--timeout "{tree_timeout}" \
|
|
497
|
+
[--hygiene-helper "{cccGitHygieneHelper}"] \
|
|
498
|
+
[--drift-helper "{checkWorkspaceDriftHelper}"]
|
|
319
499
|
```
|
|
320
500
|
|
|
321
|
-
|
|
501
|
+
Pass `--hygiene-helper` and `--drift-helper` only when they resolved. `{source_commit}` is the commit init.md §6 read from metadata.json, before §2 recorded `{target_commit}`, and `{tree_timeout}` is the time limit init.md §6b chose: the helper stops itself within it.
|
|
502
|
+
|
|
503
|
+
The helper takes the clone's `.skf-workspace.lock`, the lock create-skill also takes, waiting up to a minute, and runs the git hygiene check inside it. It then does nothing when the clone already holds `{target_commit}`; otherwise it moves the clone there only when `{checkWorkspaceDriftHelper}` confirms the clone still holds `{source_commit}` and no tracked file has local changes, taking the commit from `{source_tree}` rather than the network. It never forces a checkout and never moves a clone another run moved. When the clone is missing, it clones it first, as create-skill does.
|
|
504
|
+
|
|
505
|
+
Bind `{advance_status}` ← `status`, `{advance_skip_reason}` ← `skip_reason`, `{advance_head}` ← `head_sha`, `{advance_log}` ← `log_message` and `{advance_warnings}` ← `warnings`. Log `{advance_log}` in the evidence report and add each `{advance_warnings}` entry to `warnings[]` as `source-tree: <entry>`.
|
|
322
506
|
|
|
323
|
-
|
|
507
|
+
- **`advanced` or `ok`:** continue.
|
|
508
|
+
- **`skipped`:** add `workspace-clone-not-updated: {advance_skip_reason}; {workspace_clone} holds {advance_head or "no commit"}, so test-skill stops with workspace-drift until it holds {target_commit}` to `warnings[]`, and continue.
|
|
509
|
+
- **`{sourceTreeHelper}` does not resolve, or the command fails or prints no JSON:** add the same warning with reason `helper-unavailable`, and continue.
|
|
324
510
|
|
|
325
|
-
|
|
326
|
-
- **Context sync after --fix:** If `fixed[]` is non-empty (i.e., `--fix` modified files on disk), re-read the modified SKILL.md to update the in-context copy. This prevents silent divergence between the in-context SKILL.md and the on-disk version that report will reference. The §0 guard has already restored `description` if divergent; the re-read picks up any other fix-corrected content.
|
|
327
|
-
- If `body.max_lines` reported, prefer selective split — extract only the largest Tier 2 section(s) to `references/`, keeping Tier 1 inline (inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval). **If falling back to `npx skill-check split-body {skill_package} --write`, run it inside the §0 guard** — split-body can also touch frontmatter. Verify anchors resolve after split.
|
|
328
|
-
- Run: `npx skill-check diff` if original version was preserved.
|
|
329
|
-
- Run: `npx skill-check check {skill_package} --format json` for security scan. (Read-only; guard not required.)
|
|
511
|
+
This section never halts: the skill is written, and only the clone test-skill reads is out of step.
|
|
330
512
|
|
|
331
|
-
|
|
513
|
+
### 10. Close the Rollback Window
|
|
332
514
|
|
|
333
|
-
|
|
515
|
+
Once §9 has run, this run's writes stand: from `{project-root}`, run `uv run {runStateHelper} finish --run-dir "{run_dir}"`. It marks the run finished, then deletes the snapshot: from then on a halt undoes nothing. When it fails or prints no JSON, add `run-state-not-finished: {its message}` to `warnings[]`: no step after this one halts, and step 7 removes the run folder.
|
|
334
516
|
|
|
335
|
-
###
|
|
517
|
+
### 11. Route to Next Step
|
|
336
518
|
|
|
337
|
-
This step auto-proceeds
|
|
519
|
+
This step auto-proceeds: no user choices. Once §10 has run, display "**Proceeding to report...**", then load, fully read, and execute `{nextStepFile}` to display the change report.
|
|
338
520
|
|