bmad-module-skill-forge 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +51 -49
- package/docs/404.md +10 -8
- package/docs/_data/pinned.yaml +22 -13
- package/docs/_internal/STABILITY.md +58 -35
- package/docs/agents.md +39 -53
- package/docs/architecture.md +65 -51
- package/docs/bmad-synergy.md +53 -51
- package/docs/campaign.md +127 -75
- package/docs/concepts.md +34 -28
- package/docs/examples.md +70 -68
- package/docs/forge-auto.md +34 -31
- package/docs/getting-started.md +118 -57
- package/docs/how-it-works.md +25 -21
- package/docs/index.md +15 -15
- package/docs/skill-model.md +70 -59
- package/docs/troubleshooting.md +135 -18
- package/docs/verifying-a-skill.md +83 -57
- package/docs/why-skf.md +24 -22
- package/docs/workflows.md +274 -110
- package/package.json +18 -10
- package/src/forger/preferences.yaml +8 -1
- package/src/knowledge/agentskills-spec.md +2 -0
- package/src/knowledge/ccc-bridge.md +40 -24
- package/src/knowledge/confidence-tiers.md +9 -5
- package/src/knowledge/overview.md +3 -3
- package/src/knowledge/progressive-capability.md +8 -8
- package/src/knowledge/provenance-tracking.md +9 -5
- package/src/knowledge/qmd-registry.md +2 -2
- package/src/knowledge/skf-knowledge-index.csv +2 -2
- package/src/knowledge/skill-lifecycle.md +18 -11
- package/src/knowledge/split-body-strategy.md +0 -3
- package/src/knowledge/tool-resolution.md +4 -2
- package/src/knowledge/version-paths.md +101 -15
- package/src/module-help.csv +3 -3
- package/src/module.yaml +10 -0
- package/src/shared/data/ast-grep-recipes.yaml +608 -0
- package/src/shared/data/ide-context-files.json +30 -0
- package/src/shared/health-check.md +32 -12
- package/src/shared/references/description-guard-protocol.md +14 -9
- package/src/shared/references/feasibility-report-schema.md +19 -13
- package/src/shared/references/headless-gate-convention.md +55 -3
- package/src/shared/references/output-contract-schema.md +19 -8
- package/src/shared/references/pipeline-contracts.md +94 -29
- package/src/shared/references/tessl-review.md +77 -0
- package/src/shared/scripts/schemas/skf-analyze-result-envelope.v1.json +194 -0
- package/src/shared/scripts/schemas/skf-audit-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +32 -4
- package/src/shared/scripts/schemas/skf-campaign-result-envelope.v1.json +174 -0
- package/src/shared/scripts/schemas/skf-create-skill-result-envelope.v1.json +284 -0
- package/src/shared/scripts/schemas/skf-drop-skill-result-envelope.v1.json +164 -0
- package/src/shared/scripts/schemas/skf-export-result-envelope.v1.json +160 -0
- package/src/shared/scripts/schemas/skf-quick-skill-result-envelope.v1.json +261 -0
- package/src/shared/scripts/schemas/skf-refine-architecture-result-envelope.v1.json +192 -0
- package/src/shared/scripts/schemas/skf-rename-skill-result-envelope.v1.json +179 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +27 -11
- package/src/shared/scripts/schemas/skf-stack-result-envelope.v1.json +176 -0
- package/src/shared/scripts/schemas/skf-test-result-envelope.v1.json +173 -0
- package/src/shared/scripts/schemas/skf-update-result-envelope.v1.json +19 -5
- package/src/shared/scripts/schemas/skf-verify-stack-result-envelope.v1.json +180 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +34 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +1 -1
- package/src/shared/scripts/skf-atomic-write.py +30 -14
- package/src/shared/scripts/skf-build-change-manifest.py +2203 -24
- package/src/shared/scripts/skf-ccc-git-hygiene.py +532 -0
- package/src/shared/scripts/skf-check-unit-records.py +184 -0
- package/src/shared/scripts/skf-check-workspace-drift.py +538 -9
- package/src/shared/scripts/skf-classify-changed-files.py +694 -0
- package/src/shared/scripts/skf-comention-pairs.py +1333 -123
- package/src/shared/scripts/skf-compare-file-hashes.py +1 -1
- package/src/shared/scripts/skf-count-imports.py +1867 -0
- package/src/shared/scripts/skf-count-tokens.py +1 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +3 -2
- package/src/shared/scripts/skf-description-guard.py +117 -9
- package/src/shared/scripts/skf-detect-docs.py +892 -4
- package/src/shared/scripts/skf-detect-language.py +486 -107
- package/src/shared/scripts/skf-detect-registry.py +887 -0
- package/src/shared/scripts/skf-detect-scripts-assets.py +1 -1
- package/src/shared/scripts/skf-detect-tools.py +215 -27
- package/src/shared/scripts/skf-detect-workspaces.py +506 -30
- package/src/shared/scripts/skf-disqualify-candidates.py +821 -182
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +69 -191
- package/src/shared/scripts/skf-emit-result-envelope.py +1604 -106
- package/src/shared/scripts/skf-enumerate-stack-skills.py +986 -117
- package/src/shared/scripts/skf-extract-public-api.py +4084 -85
- package/src/shared/scripts/skf-extraction-inventory.py +746 -0
- package/src/shared/scripts/skf-extraction-snapshot.py +539 -0
- package/src/shared/scripts/skf-fetch-temporal.py +674 -0
- package/src/shared/scripts/skf-find-cycles.py +97 -29
- package/src/shared/scripts/skf-find-test-report.py +476 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +444 -203
- package/src/shared/scripts/skf-github-fetch.py +365 -0
- package/src/shared/scripts/skf-github-probe.py +661 -0
- package/src/shared/scripts/skf-hash-content.py +166 -12
- package/src/shared/scripts/skf-language-corpora.py +1 -1
- package/src/shared/scripts/skf-load-provenance.py +159 -27
- package/src/shared/scripts/skf-manifest-ops.py +1 -1
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +2307 -223
- package/src/shared/scripts/skf-merge-doc-urls.py +1 -1
- package/src/shared/scripts/skf-names-present.py +221 -0
- package/src/shared/scripts/skf-pair-intersect.py +119 -28
- package/src/shared/scripts/skf-parse-gaps.py +1384 -0
- package/src/shared/scripts/skf-preapply.py +1 -1
- package/src/shared/scripts/skf-preflight.py +161 -27
- package/src/shared/scripts/skf-promote-staged.py +430 -0
- package/src/shared/scripts/skf-provenance-gap-dispatch.py +39 -7
- package/src/shared/scripts/skf-qmd-classify-collections.py +229 -45
- package/src/shared/scripts/skf-quick-batch.py +774 -0
- package/src/shared/scripts/skf-rebuild-managed-sections.py +972 -88
- package/src/shared/scripts/skf-recommend-scope-type.py +323 -156
- package/src/shared/scripts/skf-render-metadata-stats.py +156 -12
- package/src/shared/scripts/skf-render-quick-metadata.py +132 -28
- package/src/shared/scripts/skf-render-stack-metadata.py +976 -0
- package/src/shared/scripts/skf-resolve-authoritative-files.py +128 -13
- package/src/shared/scripts/skf-resolve-package.py +523 -106
- package/src/shared/scripts/skf-rewrite-skill-name.py +433 -47
- package/src/shared/scripts/skf-run-lock.py +577 -0
- package/src/shared/scripts/skf-scan-manifests.py +998 -57
- package/src/shared/scripts/skf-scan-skill-md-structure.py +1341 -37
- package/src/shared/scripts/skf-severity-classify.py +712 -108
- package/src/shared/scripts/skf-shape-detect.py +298 -204
- package/src/shared/scripts/skf-shard-body.py +9 -8
- package/src/shared/scripts/skf-skill-inventory.py +2412 -89
- package/src/shared/scripts/skf-skills-module.py +472 -0
- package/src/shared/scripts/skf-source-tree.py +1796 -0
- package/src/shared/scripts/skf-structural-diff.py +670 -119
- package/src/shared/scripts/skf-tessl-review.py +706 -0
- package/src/shared/scripts/skf-update-active-symlink.py +5 -5
- package/src/shared/scripts/skf-update-run-state.py +624 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +92 -29
- package/src/shared/scripts/skf-validate-brief-schema.py +4 -1
- package/src/shared/scripts/skf-validate-feasibility-report.py +577 -66
- package/src/shared/scripts/skf-validate-frontmatter.py +103 -26
- package/src/shared/scripts/skf-validate-output.py +712 -64
- package/src/shared/scripts/skf-validate-pins.py +1 -1
- package/src/shared/scripts/skf-verify-no-trace.py +116 -10
- package/src/shared/scripts/skf-verify-provenance-completeness.py +2919 -54
- package/src/shared/scripts/skf-write-skill-brief.py +367 -19
- package/src/shared/tool-requirements.yaml +201 -0
- package/src/skf-analyze-source/SKILL.md +41 -36
- package/src/skf-analyze-source/assets/skill-brief-schema.md +7 -8
- package/src/skf-analyze-source/customize.toml +36 -24
- package/src/skf-analyze-source/references/auto-docs-only.md +58 -20
- package/src/skf-analyze-source/references/continue.md +2 -13
- package/src/skf-analyze-source/references/discover-additional-source.md +147 -0
- package/src/skf-analyze-source/references/generate-briefs.md +121 -55
- package/src/skf-analyze-source/references/headless-contract.md +42 -15
- package/src/skf-analyze-source/references/identify-units.md +96 -110
- package/src/skf-analyze-source/references/init.md +73 -71
- package/src/skf-analyze-source/references/map-and-detect.md +123 -105
- package/src/skf-analyze-source/references/map-unit-exports.md +68 -0
- package/src/skf-analyze-source/references/recommend.md +51 -70
- package/src/skf-analyze-source/references/scan-project.md +60 -84
- package/src/skf-analyze-source/references/scan-root.md +39 -0
- package/src/skf-analyze-source/references/step-auto-scope-coexistence.md +47 -0
- package/src/skf-analyze-source/references/step-auto-scope-corpora.md +53 -0
- package/src/skf-analyze-source/references/step-auto-scope-split.md +91 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +208 -293
- package/src/skf-analyze-source/references/step-shape-detect.md +14 -12
- package/src/skf-analyze-source/references/unit-detection-heuristics.md +37 -42
- package/src/skf-audit-skill/SKILL.md +43 -51
- package/src/skf-audit-skill/assets/drift-report-template.md +27 -3
- package/src/skf-audit-skill/customize.toml +41 -20
- package/src/skf-audit-skill/references/constituent-freshness.md +91 -0
- package/src/skf-audit-skill/references/doc-drift.md +141 -0
- package/src/skf-audit-skill/references/headless-contract.md +29 -0
- package/src/skf-audit-skill/references/init.md +148 -210
- package/src/skf-audit-skill/references/re-index.md +96 -108
- package/src/skf-audit-skill/references/report.md +96 -33
- package/src/skf-audit-skill/references/semantic-diff.md +27 -18
- package/src/skf-audit-skill/references/severity-classify.md +79 -65
- package/src/skf-audit-skill/references/severity-rules.md +15 -6
- package/src/skf-audit-skill/references/structural-diff.md +75 -90
- package/src/skf-audit-skill/references/upstream-checkout.md +77 -0
- package/src/skf-audit-skill/scripts/render-drift-tables.py +685 -0
- package/src/skf-brief-skill/SKILL.md +36 -25
- package/src/skf-brief-skill/assets/description-voice-examples.md +1 -1
- package/src/skf-brief-skill/assets/scope-templates.md +17 -24
- package/src/skf-brief-skill/assets/skill-brief-schema.md +29 -15
- package/src/skf-brief-skill/customize.toml +48 -20
- package/src/skf-brief-skill/references/analyze-target.md +142 -125
- package/src/skf-brief-skill/references/confirm-brief.md +10 -8
- package/src/skf-brief-skill/references/draft-checkpoint.md +36 -17
- package/src/skf-brief-skill/references/gather-intent-ratify.md +68 -0
- package/src/skf-brief-skill/references/gather-intent.md +87 -182
- package/src/skf-brief-skill/references/headless-args.md +84 -16
- package/src/skf-brief-skill/references/health-check.md +1 -1
- package/src/skf-brief-skill/references/invocation-contract.md +18 -13
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +1 -1
- package/src/skf-brief-skill/references/qmd-collection-registration.md +7 -10
- package/src/skf-brief-skill/references/scope-definition.md +123 -47
- package/src/skf-brief-skill/references/step-auto-brief.md +24 -80
- package/src/skf-brief-skill/references/step-auto-validate.md +20 -121
- package/src/skf-brief-skill/references/version-resolution.md +3 -2
- package/src/skf-brief-skill/references/write-brief.md +66 -57
- package/src/skf-campaign/SKILL.md +31 -34
- package/src/skf-campaign/assets/campaign-state-schema.json +25 -4
- package/src/skf-campaign/customize.toml +47 -16
- package/src/skf-campaign/references/campaign-contracts.md +39 -29
- package/src/skf-campaign/references/campaign-directive-spec.md +4 -2
- package/src/skf-campaign/references/health-check.md +7 -16
- package/src/skf-campaign/references/step-01-setup.md +46 -84
- package/src/skf-campaign/references/step-02-strategy.md +31 -30
- package/src/skf-campaign/references/step-03-pins.md +26 -18
- package/src/skf-campaign/references/step-04-provenance.md +24 -23
- package/src/skf-campaign/references/step-05-skill-loop.md +67 -48
- package/src/skf-campaign/references/step-06-batch.md +41 -34
- package/src/skf-campaign/references/step-07-capstone.md +40 -25
- package/src/skf-campaign/references/step-08-verify.md +28 -29
- package/src/skf-campaign/references/step-09-refine.md +29 -30
- package/src/skf-campaign/references/step-10-export.md +48 -36
- package/src/skf-campaign/references/step-11-maintenance.md +31 -34
- package/src/skf-campaign/references/step-resume.md +37 -68
- package/src/skf-campaign/scripts/campaign-deps.py +57 -18
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +361 -54
- package/src/skf-campaign/scripts/campaign-provenance.py +96 -53
- package/src/skf-campaign/scripts/campaign-quality-gate.py +592 -0
- package/src/skf-campaign/scripts/campaign-render-batch.py +257 -49
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +343 -50
- package/src/skf-campaign/scripts/campaign-report.py +204 -37
- package/src/skf-campaign/scripts/campaign-state.py +1128 -0
- package/src/skf-campaign/scripts/campaign-status.py +10 -74
- package/src/skf-campaign/scripts/campaign-validate-pins.py +30 -3
- package/src/skf-campaign/scripts/campaign-validate-state.py +34 -4
- package/src/skf-campaign/templates/campaign-brief-template.yaml +4 -5
- package/src/skf-campaign/templates/campaign-report-template.md +5 -1
- package/src/skf-campaign/templates/kickoff-template.md +0 -14
- package/src/skf-create-skill/SKILL.md +27 -20
- package/src/skf-create-skill/assets/compile-assembly-rules.md +6 -5
- package/src/skf-create-skill/assets/skill-sections.md +17 -6
- package/src/skf-create-skill/customize.toml +20 -8
- package/src/skf-create-skill/references/authoritative-files-protocol.md +41 -42
- package/src/skf-create-skill/references/batch-mode.md +81 -0
- package/src/skf-create-skill/references/ccc-index-check.md +27 -0
- package/src/skf-create-skill/references/compile.md +107 -29
- package/src/skf-create-skill/references/component-extraction.md +113 -91
- package/src/skf-create-skill/references/enrich.md +41 -23
- package/src/skf-create-skill/references/entry-points-by-hand.md +20 -0
- package/src/skf-create-skill/references/extract.md +200 -161
- package/src/skf-create-skill/references/extraction-patterns-by-hand.md +141 -0
- package/src/skf-create-skill/references/extraction-patterns-tracing.md +2 -2
- package/src/skf-create-skill/references/extraction-patterns.md +584 -196
- package/src/skf-create-skill/references/generate-artifacts.md +145 -93
- package/src/skf-create-skill/references/health-check.md +1 -1
- package/src/skf-create-skill/references/load-brief.md +53 -36
- package/src/skf-create-skill/references/relabel-rule.md +12 -0
- package/src/skf-create-skill/references/report.md +28 -59
- package/src/skf-create-skill/references/source-resolution-protocols.md +49 -155
- package/src/skf-create-skill/references/step-auto-shard.md +20 -93
- package/src/skf-create-skill/references/step-doc-rot.md +44 -60
- package/src/skf-create-skill/references/step-doc-sources.md +55 -18
- package/src/skf-create-skill/references/sub/ccc-discover.md +18 -7
- package/src/skf-create-skill/references/sub/fetch-docs.md +106 -57
- package/src/skf-create-skill/references/sub/fetch-temporal.md +53 -166
- package/src/skf-create-skill/references/tier-degradation-rules.md +18 -9
- package/src/skf-create-skill/references/validate.md +173 -99
- package/src/skf-create-skill/scripts/scan-doc-rot.py +232 -99
- package/src/skf-create-stack-skill/SKILL.md +29 -56
- package/src/skf-create-stack-skill/assets/metadata-contract.md +51 -0
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +15 -15
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +23 -57
- package/src/skf-create-stack-skill/customize.toml +23 -12
- package/src/skf-create-stack-skill/references/compile-stack.md +57 -66
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +24 -44
- package/src/skf-create-stack-skill/references/detect-integrations.md +92 -95
- package/src/skf-create-stack-skill/references/detect-manifests.md +46 -71
- package/src/skf-create-stack-skill/references/generate-output.md +138 -128
- package/src/skf-create-stack-skill/references/init.md +90 -32
- package/src/skf-create-stack-skill/references/integration-patterns.md +1 -17
- package/src/skf-create-stack-skill/references/invocation-contract.md +34 -0
- package/src/skf-create-stack-skill/references/manifest-patterns.md +32 -20
- package/src/skf-create-stack-skill/references/parallel-extract.md +112 -38
- package/src/skf-create-stack-skill/references/rank-and-confirm.md +57 -75
- package/src/skf-create-stack-skill/references/report.md +39 -33
- package/src/skf-create-stack-skill/references/validate.md +20 -39
- package/src/skf-drop-skill/SKILL.md +28 -51
- package/src/skf-drop-skill/customize.toml +44 -30
- package/src/skf-drop-skill/references/execute.md +79 -146
- package/src/skf-drop-skill/references/invocation-contract.md +64 -0
- package/src/skf-drop-skill/references/report.md +46 -30
- package/src/skf-drop-skill/references/select.md +118 -124
- package/src/skf-drop-skill/scripts/dir-sizes.py +54 -26
- package/src/skf-drop-skill/scripts/drop-roster.py +322 -0
- package/src/skf-export-skill/SKILL.md +40 -62
- package/src/skf-export-skill/assets/managed-section-format.md +14 -50
- package/src/skf-export-skill/assets/snippet-format.md +3 -1
- package/src/skf-export-skill/customize.toml +42 -13
- package/src/skf-export-skill/references/generate-snippet.md +54 -46
- package/src/skf-export-skill/references/health-check.md +2 -0
- package/src/skf-export-skill/references/invocation-contract.md +15 -0
- package/src/skf-export-skill/references/load-skill.md +88 -62
- package/src/skf-export-skill/references/manifest-rebuild.md +9 -10
- package/src/skf-export-skill/references/multi-skill-mode.md +4 -4
- package/src/skf-export-skill/references/orphan-context-detection.md +13 -5
- package/src/skf-export-skill/references/orphan-row-detection.md +31 -22
- package/src/skf-export-skill/references/package.md +15 -73
- package/src/skf-export-skill/references/preflight-snippet-root-probe.md +51 -70
- package/src/skf-export-skill/references/result-envelope.md +31 -16
- package/src/skf-export-skill/references/summary.md +41 -17
- package/src/skf-export-skill/references/token-report.md +4 -12
- package/src/skf-export-skill/references/update-context.md +118 -237
- package/src/skf-forger/SKILL.md +61 -37
- package/src/skf-forger/bmad-skill-manifest.yaml +4 -1
- package/src/skf-forger/customize.toml +43 -0
- package/src/skf-forger/references/pipeline-mode.md +73 -18
- package/src/skf-forger/scripts/forge-status.py +299 -0
- package/src/skf-forger/scripts/parse-pipeline.py +322 -54
- package/src/skf-forger/scripts/pipeline-gate.py +428 -0
- package/src/skf-forger/scripts/pipeline-journal.py +1052 -0
- package/src/skf-quick-skill/SKILL.md +54 -53
- package/src/skf-quick-skill/assets/skill-template.md +4 -2
- package/src/skf-quick-skill/customize.toml +51 -20
- package/src/skf-quick-skill/references/batch-mode.md +74 -21
- package/src/skf-quick-skill/references/compile.md +30 -31
- package/src/skf-quick-skill/references/ecosystem-check.md +7 -57
- package/src/skf-quick-skill/references/finalize.md +69 -18
- package/src/skf-quick-skill/references/halt-contract.md +41 -29
- package/src/skf-quick-skill/references/health-check.md +5 -3
- package/src/skf-quick-skill/references/quick-extract.md +114 -45
- package/src/skf-quick-skill/references/registry-resolution.md +9 -64
- package/src/skf-quick-skill/references/resolve-target.md +116 -66
- package/src/skf-quick-skill/references/write-and-validate.md +79 -40
- package/src/skf-refine-architecture/SKILL.md +43 -53
- package/src/skf-refine-architecture/customize.toml +49 -20
- package/src/skf-refine-architecture/references/compile.md +97 -54
- package/src/skf-refine-architecture/references/exit-codes.md +30 -0
- package/src/skf-refine-architecture/references/finding-storage.md +21 -0
- package/src/skf-refine-architecture/references/gap-analysis.md +101 -38
- package/src/skf-refine-architecture/references/improvements.md +14 -20
- package/src/skf-refine-architecture/references/init.md +98 -57
- package/src/skf-refine-architecture/references/issue-detection.md +32 -39
- package/src/skf-refine-architecture/references/refinement-rules.md +37 -48
- package/src/skf-refine-architecture/references/report.md +65 -54
- package/src/skf-refine-architecture/scripts/skf-check-preservation.py +1619 -0
- package/src/skf-rename-skill/SKILL.md +19 -54
- package/src/skf-rename-skill/customize.toml +32 -26
- package/src/skf-rename-skill/references/execute.md +230 -184
- package/src/skf-rename-skill/references/exit-codes.md +7 -7
- package/src/skf-rename-skill/references/health-check.md +1 -7
- package/src/skf-rename-skill/references/invocation-contract.md +25 -0
- package/src/skf-rename-skill/references/report.md +63 -52
- package/src/skf-rename-skill/references/select.md +197 -115
- package/src/skf-rename-skill/scripts/skf-validate-rename-name.py +161 -21
- package/src/skf-setup/SKILL.md +26 -26
- package/src/skf-setup/customize.toml +14 -10
- package/src/skf-setup/references/auto-index.md +50 -63
- package/src/skf-setup/references/ccc-index.md +29 -89
- package/src/skf-setup/references/detect-and-tier.md +58 -66
- package/src/skf-setup/references/health-check.md +2 -0
- package/src/skf-setup/references/invocation-contract.md +14 -0
- package/src/skf-setup/references/report.md +43 -166
- package/src/skf-setup/references/tier-rules.md +5 -5
- package/src/skf-setup/references/write-config.md +34 -57
- package/src/skf-test-skill/SKILL.md +17 -57
- package/src/skf-test-skill/assets/output-section-formats.md +33 -34
- package/src/skf-test-skill/customize.toml +27 -13
- package/src/skf-test-skill/references/coherence-check.md +134 -62
- package/src/skf-test-skill/references/coverage-check-tiers.md +35 -0
- package/src/skf-test-skill/references/coverage-check.md +217 -254
- package/src/skf-test-skill/references/detect-mode.md +5 -5
- package/src/skf-test-skill/references/external-validators.md +94 -82
- package/src/skf-test-skill/references/health-check.md +6 -7
- package/src/skf-test-skill/references/init.md +163 -88
- package/src/skf-test-skill/references/invocation-contract.md +66 -0
- package/src/skf-test-skill/references/migration-section-rules.md +5 -2
- package/src/skf-test-skill/references/report.md +162 -142
- package/src/skf-test-skill/references/score.md +80 -107
- package/src/skf-test-skill/references/scoring-rules.md +41 -155
- package/src/skf-test-skill/references/source-access-protocol.md +19 -92
- package/src/skf-test-skill/references/step-hard-gate.md +51 -33
- package/src/skf-test-skill/scripts/aggregate-coherence.py +218 -30
- package/src/skf-test-skill/scripts/build-result-context.py +287 -0
- package/src/skf-test-skill/scripts/check-metadata-coherence.py +37 -4
- package/src/skf-test-skill/scripts/combine-external-scores.py +23 -6
- package/src/skf-test-skill/scripts/compute-score.py +223 -45
- package/src/skf-test-skill/scripts/gap-ledger.py +1381 -0
- package/src/skf-test-skill/scripts/hard-gate.py +203 -0
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +760 -0
- package/src/skf-test-skill/scripts/locate-export-segments.py +835 -0
- package/src/skf-test-skill/scripts/reconcile-coverage.py +273 -79
- package/src/skf-test-skill/scripts/score-signatures.py +459 -0
- package/src/skf-test-skill/scripts/stage-helper-payload.py +148 -0
- package/src/skf-test-skill/scripts/validate-inventory.py +311 -16
- package/src/skf-test-skill/scripts/verify-declared-numerator.py +127 -52
- package/src/skf-test-skill/templates/test-report-template.md +21 -15
- package/src/skf-update-skill/SKILL.md +38 -25
- package/src/skf-update-skill/customize.toml +27 -12
- package/src/skf-update-skill/references/detect-changes.md +233 -222
- package/src/skf-update-skill/references/gap-driven.md +336 -0
- package/src/skf-update-skill/references/health-check.md +31 -5
- package/src/skf-update-skill/references/init.md +250 -84
- package/src/skf-update-skill/references/invocation-contract.md +12 -0
- package/src/skf-update-skill/references/manual-section-rules.md +6 -23
- package/src/skf-update-skill/references/merge.md +180 -47
- package/src/skf-update-skill/references/re-extract.md +53 -262
- package/src/skf-update-skill/references/report.md +100 -35
- package/src/skf-update-skill/references/write.md +331 -149
- package/src/skf-update-skill/scripts/skf-new-file-diff.py +29 -7
- package/src/skf-verify-stack/SKILL.md +23 -42
- package/src/skf-verify-stack/assets/feasibility-report-template.md +11 -5
- package/src/skf-verify-stack/customize.toml +55 -22
- package/src/skf-verify-stack/references/coverage-patterns.md +17 -8
- package/src/skf-verify-stack/references/coverage.md +67 -64
- package/src/skf-verify-stack/references/exit-codes.md +21 -8
- package/src/skf-verify-stack/references/init.md +107 -62
- package/src/skf-verify-stack/references/integration-verification-rules.md +12 -13
- package/src/skf-verify-stack/references/integrations.md +106 -65
- package/src/skf-verify-stack/references/report.md +59 -42
- package/src/skf-verify-stack/references/requirements.md +32 -9
- package/src/skf-verify-stack/references/synthesize.md +53 -39
- package/src/skf-verify-stack/scripts/skf-coverage-tally.py +398 -52
- package/src/skf-verify-stack/scripts/skf-previous-report.py +330 -0
- package/src/skf-verify-stack/scripts/skf-report-delta.py +453 -49
- package/src/skf-verify-stack/scripts/skf-verdict-rollup.py +240 -27
- package/tools/cli/commands/install.js +11 -4
- package/tools/cli/commands/status.js +15 -3
- package/tools/cli/commands/update.js +11 -4
- package/tools/cli/lib/tool-check.js +320 -0
- package/tools/cli/lib/version-check.js +22 -9
- package/tools/cli/skf-cli.js +4 -1
- package/.gitattributes +0 -16
- package/.nvmrc +0 -1
- package/docs/_internal/RELEASING.md +0 -645
- package/release-audits/v1.0.0-launch-audit.md +0 -728
- package/src/skf-audit-skill/references/step-doc-drift.md +0 -177
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +0 -26
- package/src/skf-campaign/manifest.yaml +0 -15
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +0 -106
- package/src/skf-create-skill/references/ecosystem-check.md +0 -74
- package/src/skf-drop-skill/references/headless-contract.md +0 -18
- package/src/skf-update-skill/references/merge-conflict-rules.md +0 -15
- package/src/skf-update-skill/references/remote-source-resolution.md +0 -98
- package/src/skf-update-skill/references/validate.md +0 -124
- package/tools/validate-docs-drift.js +0 -233
- package/tools/validate-docs-links.js +0 -665
|
@@ -1,45 +1,52 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: 're-extract.md'
|
|
3
3
|
noChangeReportFile: 'report.md'
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
# SHA-256 hashing (file-read + size + line-count) and provenance comparison
|
|
7
|
-
# (UNCHANGED / MODIFIED_FILE / DELETED_FILE classification). Falling back to
|
|
8
|
-
# prose-driven hashing would lose hash stability across runs.
|
|
4
|
+
extractionPatternsData: 'skf-create-skill/references/extraction-patterns.md'
|
|
5
|
+
# `{hashContentHelper}`: Category D's hash comparison. HALT if neither exists.
|
|
9
6
|
hashContentProbeOrder:
|
|
10
7
|
- '{project-root}/_bmad/skf/shared/scripts/skf-hash-content.py'
|
|
11
8
|
- '{project-root}/src/shared/scripts/skf-hash-content.py'
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# the major-version trigger. Falling back to prose-driven count rollups
|
|
16
|
-
# would let the LLM drift on manifest shape across runs.
|
|
9
|
+
# `{buildChangeManifestHelper}`: §3's `build` (and §1's for a docs-only
|
|
10
|
+
# skill), §2.2's `deletion-ratio` and Category C's `rename-candidates`. HALT
|
|
11
|
+
# if neither exists.
|
|
17
12
|
buildChangeManifestProbeOrder:
|
|
18
13
|
- '{project-root}/_bmad/skf/shared/scripts/skf-build-change-manifest.py'
|
|
19
14
|
- '{project-root}/src/shared/scripts/skf-build-change-manifest.py'
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# Out-of-Scope candidates, and classify them against brief.scope.amendments[]
|
|
23
|
-
# in one call. Falling back to prose-driven markdown parsing would let
|
|
24
|
-
# report-format drift produce silent skips of out-of-scope candidates.
|
|
15
|
+
# `{provenanceGapDispatchHelper}`: §1c's drift-report candidates. HALT if
|
|
16
|
+
# neither exists.
|
|
25
17
|
provenanceGapDispatchProbeOrder:
|
|
26
18
|
- '{project-root}/_bmad/skf/shared/scripts/skf-provenance-gap-dispatch.py'
|
|
27
19
|
- '{project-root}/src/shared/scripts/skf-provenance-gap-dispatch.py'
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# source tree for scripts/assets (directory conventions, shebang signals,
|
|
31
|
-
# `package.json` `bin` entry-points, asset filename patterns) so NEW_FILE
|
|
32
|
-
# detection mirrors create-skill §4c exactly. Falling back to prose-driven
|
|
33
|
-
# file walking would let the LLM drift on the heuristic list and miss new
|
|
34
|
-
# scripts/assets at deeper directory depths — and on installed modules
|
|
35
|
-
# (no `src/` tree) the LLM would otherwise guess a non-existent path.
|
|
20
|
+
# `{detectScriptsAssetsHelper}`: Category D's script and asset walk, as
|
|
21
|
+
# create-skill's. HALT if neither exists.
|
|
36
22
|
detectScriptsAssetsProbeOrder:
|
|
37
23
|
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-scripts-assets.py'
|
|
38
24
|
- '{project-root}/src/shared/scripts/skf-detect-scripts-assets.py'
|
|
39
|
-
# `{newFileDiffHelper}
|
|
40
|
-
#
|
|
41
|
-
# otherwise ask the model to compute by hand. Per-skill helper, always bundled.
|
|
25
|
+
# `{newFileDiffHelper}`: Category D's NEW_FILE set difference, bundled with
|
|
26
|
+
# this skill.
|
|
42
27
|
newFileDiffHelper: 'scripts/skf-new-file-diff.py'
|
|
28
|
+
# `{resolveAuthoritativeFilesHelper}`: §1b's mirror. HALT if neither exists.
|
|
29
|
+
resolveAuthoritativeFilesProbeOrder:
|
|
30
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-resolve-authoritative-files.py'
|
|
31
|
+
- '{project-root}/src/shared/scripts/skf-resolve-authoritative-files.py'
|
|
32
|
+
# `{classifyChangedFilesHelper}`: §2.1 Category A. HALT if neither exists.
|
|
33
|
+
classifyChangedFilesProbeOrder:
|
|
34
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-classify-changed-files.py'
|
|
35
|
+
- '{project-root}/src/shared/scripts/skf-classify-changed-files.py'
|
|
36
|
+
# `{extractPublicApiHelper}`: §2.1 Category B's recipe runner. If neither
|
|
37
|
+
# exists, read the files by eye.
|
|
38
|
+
extractPublicApiProbeOrder:
|
|
39
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
|
|
40
|
+
- '{project-root}/src/shared/scripts/skf-extract-public-api.py'
|
|
41
|
+
# `{structuralDiffHelper}`: §2.1 Category B's diff. HALT if neither exists.
|
|
42
|
+
structuralDiffProbeOrder:
|
|
43
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-structural-diff.py'
|
|
44
|
+
- '{project-root}/src/shared/scripts/skf-structural-diff.py'
|
|
45
|
+
# `{compareDocHashesHelper}`: §1's docs-only branch, the doc-hash comparison
|
|
46
|
+
# audit-skill's doc-drift step runs. HALT if neither exists.
|
|
47
|
+
compareDocHashesProbeOrder:
|
|
48
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-detect-docs.py'
|
|
49
|
+
- '{project-root}/src/shared/scripts/skf-detect-docs.py'
|
|
43
50
|
---
|
|
44
51
|
|
|
45
52
|
<!-- Config: communicate in {communication_language}. -->
|
|
@@ -52,60 +59,49 @@ Compare current source code state against the provenance map to produce a comple
|
|
|
52
59
|
|
|
53
60
|
## Rules
|
|
54
61
|
|
|
55
|
-
- Focus only on detecting and classifying changes
|
|
56
|
-
-
|
|
62
|
+
- Focus only on detecting and classifying changes: extract only what Category B's diff reads (step 3 reuses it), and merge nothing
|
|
63
|
+
- Run §2.1's categories in order, A, then B, then C: each reads the one before. Fan Category B's reading out in parallel batches (Pattern 4) only when its file list is long
|
|
57
64
|
|
|
58
65
|
## Steps
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
The helpers below and step 3 pass their JSON to each other through `{run_dir}`, the run folder SKILL.md On Activation created.
|
|
61
68
|
|
|
62
|
-
**
|
|
69
|
+
**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. Nothing this step wrote is undone: it writes the skill brief only with a person's decision or a headless deferral (§1b, §1c), both of which stand, and the read-only modes write a copy in `{run_dir}` instead.
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
```bash
|
|
72
|
+
uv run {runStateHelper} halt --run-dir "{run_dir}" \
|
|
73
|
+
[--tree "{source_tree}"] \
|
|
74
|
+
[--lock "{forge_data_folder}/{skill_name}/.skf-update.lock" --owner "{lock_owner}"] \
|
|
75
|
+
[--emit] <<'SKF_JSON'
|
|
76
|
+
{"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>", "previous_version": "<the same>", "update_mode": "<normal or degraded>"}
|
|
77
|
+
SKF_JSON
|
|
78
|
+
```
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
2. Read the **Coverage Analysis** section — each per-export row has documented/missing/mismatch status
|
|
68
|
-
3. Translate findings into change manifest format:
|
|
80
|
+
Pass `--tree` when init.md §6b bound `{source_tree}`, `--lock` and `--owner` when init.md §1b bound `{lock_owner}` (the read-only modes take no lock), and `--emit` in `{headless_mode}`. 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 (`source-tree-not-removed`, `run-lock-not-released`); it never stops on a result. 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: "detect-changes:<the helper's file name>"` and `reason: "<the helper's file name> is missing; re-install SKF"`.
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|-------------|----------|-----------------|
|
|
72
|
-
| Critical | Missing export documentation | NEW_EXPORT (undocumented public API) — unless the remediation says the export is internal / out of scope; then DELETED_EXPORT (rescope), see rule R1 |
|
|
73
|
-
| High | Signature mismatch | MODIFIED_EXPORT (signature needs update) |
|
|
74
|
-
| Medium | Missing type/interface docs | NEW_EXPORT (undocumented type) |
|
|
75
|
-
| Medium | Stale documentation | MODIFIED_EXPORT (docs reference removed export) |
|
|
76
|
-
| Critical/Medium | Coverage gap whose remediation is **removal** (export is internal, `#[doc(hidden)]`, or explicitly out of scope) | DELETED_EXPORT (rescope) — see rule R1 |
|
|
77
|
-
| Medium/High | Structural / coherence drift (output file only, no source change) | STRUCTURAL_FIX — see rule R2 |
|
|
78
|
-
| Medium | Export documented in SKILL.md/references but **missing from the provenance-map** | NEW_EXPORT (provenance-completeness) — see rule R3 |
|
|
79
|
-
| Low | Missing metadata/examples | metadata update — see rule R4 |
|
|
82
|
+
The halt leaves `{run_dir}` in place, with the decisions the gates below recorded before it.
|
|
80
83
|
|
|
81
|
-
**
|
|
84
|
+
**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`.
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
- **R2 — STRUCTURAL_FIX.** A coherence finding from `skf-scan-skill-md-structure.py` (e.g., `table_drift`, `unbalanced_fences`, a broken intra-skill anchor) that touches the generated output file only, with no source change and no provenance entry change. Carries `remediation` text describing the surgical markdown edit. Routes to merge Priority 8 (generated-markdown edit only); it never adds, modifies, or removes a provenance `entries[]` row.
|
|
85
|
-
- **R3 — provenance-completeness.** An export documented in SKILL.md/`references/` but absent from the provenance-map. These are documented-and-known, so `unknown` is never correct: route to re-extract §0a source resolution **regardless of severity**, gated only on `source_root` being pinned and readable (see re-extract.md §0).
|
|
86
|
-
- **R4 — metadata update.** A metadata-coherence patch that changes no export's source (e.g., reconcile a divergent `stats` count). Routes to merge Priority 8b and is applied by write.md §2 *before* the automatic stat recount (see merge.md §3 / write.md §2).
|
|
86
|
+
### 1. Scan Current Source State
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
**A docs-only skill** (`source_type: "docs-only"` in the brief or metadata.json) has no source tree for §2's categories to read: its drift is in the documents `doc_sources` records with their hashes. Compare them with the helper audit-skill's doc-drift step runs: resolve `{compareDocHashesHelper}` ← first existing path in `{compareDocHashesProbeOrder}` and, from `{project-root}`, run:
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- **`change_category`** — the Change Category resolved from the table above (`NEW_EXPORT`, `MODIFIED_EXPORT`, `DELETED_EXPORT`, `STRUCTURAL_FIX`, or `metadata update`). Step 3 §1a partitions on this field; merge.md §3 dispatches on it.
|
|
94
|
-
- **`remediation`** — the finding's full `Remediation:` text, verbatim. Required for `STRUCTURAL_FIX` (the surgical markdown edit), `metadata update` (the patch description), and `DELETED_EXPORT` rescope (the removal rationale recorded in the `scope.amendments[]` entry). For `NEW_EXPORT` / `MODIFIED_EXPORT` it is informational.
|
|
95
|
-
- **`provenance_completeness: true`** — set on a `NEW_EXPORT` entry whose gap is rule R3 (documented in SKILL.md/`references/` but absent from the provenance-map). Step 3 §0 routes these to §0a regardless of severity.
|
|
96
|
-
5. Set `gap_count` from the total number of translated entries
|
|
97
|
-
6. **Skip to section 5** (Display Change Summary) with the gap-derived manifest
|
|
90
|
+
```bash
|
|
91
|
+
uv run {compareDocHashesHelper} compare-hashes "{skill_package}/metadata.json" > "{run_dir}/doc-hashes.json"
|
|
92
|
+
```
|
|
98
93
|
|
|
99
|
-
"
|
|
94
|
+
It fetches each URL with a recorded hash and buckets it `changed`, `unchanged`, `fetch_failed` or `skipped_null_hash`, with `stats`: never fetch or hash a URL by hand. On exit 2, no JSON, or no candidate resolves, HALT with status `blocked` (halt procedure: `phase: "detect-changes:doc-hashes"`, `path: "{skill_package}/metadata.json"`, `reason: "<its error, or skf-detect-docs.py is missing; re-install SKF>"`). Add `doc-fetch-failed: {url} ({reason})` to `warnings[]` for each `fetch_failed` entry (a document that could not be read is not drift), `doc-not-hashed: {url}` for each `skipped_null_hash` entry, and `doc-drift-not-checked: metadata.json records no doc_sources` when `stats.total_tracked` is 0. Then skip §1b to §2.2 and §3's build, and have the helper write the change manifest from that output, never by hand:
|
|
100
95
|
|
|
101
|
-
|
|
96
|
+
```bash
|
|
97
|
+
uv run {buildChangeManifestHelper} build --doc-hashes "{run_dir}/doc-hashes.json" > "{run_dir}/change-manifest.json"
|
|
98
|
+
```
|
|
102
99
|
|
|
103
|
-
|
|
100
|
+
It writes `{"mode": "docs-only", "no_changes", "changed_urls", "fetch_failed", "counts": {"docs_changed", "docs_fetch_failed"}}`. On exit 1 or no JSON: HALT with status `blocked` (halt procedure: `phase: "detect-changes:change-manifest"`, its stderr as `reason`). §4 then reports no change only when every hashed document matches (`changed_urls` is empty); otherwise §5 routes to step 3, whose §1 re-fetches the changed URLs, and step 5 records their new hashes from `{run_dir}/doc-hashes.json`.
|
|
101
|
+
|
|
102
|
+
`{source_root}` is the tree init.md §6b prepared at `{target_commit}` when `{source_tree_status}` is `ready` or `offline`, and otherwise the skill's local source. **If `{source_tree_status}` is `ready` or `offline` and `{source_root}` no longer exists**, HALT with status `blocked` per SKILL.md's source-tree rule (halt procedure: `phase: "detect-changes:source-tree-missing"`, `path: "{source_root}"`, `reason: "source tree {source_root} disappeared mid-run"`): reading a missing tree would report every file deleted. §2.1's helpers walk `{source_root}` themselves: build no file inventory here.
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
- For each source file: record path, file size, last modified timestamp
|
|
107
|
-
- Focus on file types relevant to the skill (from provenance map file patterns)
|
|
108
|
-
- Exclude non-source files (node_modules, build artifacts, etc.)
|
|
104
|
+
**The brief this run reads.** Bind `{brief_path}` ← `{forge_data_folder}/{skill_name}/skill-brief.yaml`. When `detect_only_mode` or `dry_run_mode` is true and that file exists, copy it to `{run_dir}/skill-brief.yaml` and bind `{brief_path}` to the copy: §1b and §1c write each decision there, Category A reads the scope from there, and the brief itself stays as it was. Each amendment a read-only run writes to the copy is a proposed one: add `proposed-amendment: {action} {path} ({category}); not written: {--dry-run or --detect-only}` to `warnings[]` as you write it, and step 6 lists them with the change manifest.
|
|
109
105
|
|
|
110
106
|
### 1b. Discovered Authoritative Files Protocol (Mirror)
|
|
111
107
|
|
|
@@ -113,26 +109,30 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
113
109
|
|
|
114
110
|
**Skip this section entirely if:**
|
|
115
111
|
|
|
116
|
-
- `
|
|
117
|
-
- `
|
|
112
|
+
- `metadata.json.source_type == "docs-only"` (no source tree to scan), OR
|
|
113
|
+
- `{brief_path}` does not exist (a skill built without a brief, such as a quick skill): there is no scope or amendment to match a document against, and no brief to amend. Display `"Authoritative files mirror: skipped (no skill brief)."`
|
|
114
|
+
|
|
115
|
+
**Procedure (the helper and buckets of create-skill §2a):**
|
|
118
116
|
|
|
119
|
-
**
|
|
117
|
+
1. **Scan, classify and hash in one call.** Resolve `{resolveAuthoritativeFilesHelper}` ← first existing path in `{resolveAuthoritativeFilesProbeOrder}` and, from `{project-root}`, run:
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
```bash
|
|
120
|
+
uv run {resolveAuthoritativeFilesHelper} resolve \
|
|
121
|
+
--source-root "{source_root}" \
|
|
122
|
+
--brief "{brief_path}" \
|
|
123
|
+
[--provenance-map "{provenance_map_path}"]
|
|
124
|
+
```
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
- **Already in provenance map** (`entries[].source_file` or `file_entries[].source_file` contains this path): the file is already tracked. §2 will detect any drift in the normal flow. No action in §1b.
|
|
128
|
-
- **Not in provenance map:** continue to amendment check.
|
|
126
|
+
Pass `--provenance-map` whenever init.md §4 found one. It finds the files create-skill §2a looks for (`llms.txt`, `AGENTS.md`, `.cursorrules` and the like), sorts each into a bucket below, and hashes and previews each one a decision needs. Never walk, match or hash these files by hand. On exit 1 (a source root, brief or provenance map it cannot read), no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "detect-changes:authoritative-files"`, `reason: "<its stderr>"`), showing its stderr.
|
|
129
127
|
|
|
130
|
-
|
|
131
|
-
- **`
|
|
132
|
-
- **`
|
|
133
|
-
-
|
|
128
|
+
2. **Apply each bucket:**
|
|
129
|
+
- **`already_tracked[]`:** the provenance map names the file (`entries[]` or `file_entries[]`), so §2 detects its drift. No action here.
|
|
130
|
+
- **`already_in_scope[]`:** the brief's scope already takes the file (a `scope.include` glob matches it, or its latest amendment promoted it), but the map has no row for it. Add the helper's `{path, heuristic, size_bytes, line_count, content_hash}` to `promoted_docs_new[]` (the list [P] Promote below fills), with no prompt, as create-skill §2a does. Display: `"In scope: {path} scheduled for file_entries write."`
|
|
131
|
+
- **`pre_decided[]` with `prior_action: "promoted"`:** a prior run promoted it, but its `file_entries[]` row is missing (e.g. provenance-map was regenerated from source without re-reading amendments). Add it to `promoted_docs_new[]` with the helper's hash, size and line count so §4 merge writes a new `file_entries[]` row. No user prompt: the decision was already made. Display: `"Honoring prior amendment: promoted {path} scheduled for file_entries write."`
|
|
132
|
+
- **`pre_decided[]` with `prior_action: "skipped"`:** a person declined promotion before. Honor the skip silently. No prompt, no action.
|
|
133
|
+
- **`unresolved[]`:** no one has decided the file yet (a `prior_action` of `deferred-headless` marks one a headless run met and left for a person, the legacy `skipped` amendment with reason `headless: no user to prompt` included): continue to the prompt.
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
3. **Prompt.** For each `unresolved[]` candidate, present the same prompt as create-skill §2a, with the helper's `path`, `line_count`, `size_bytes`, `heuristic` and `preview` as it reported them (never recomputed):
|
|
136
136
|
|
|
137
137
|
```
|
|
138
138
|
**New authoritative file discovered since skill creation**
|
|
@@ -152,36 +152,28 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
152
152
|
[U] Update — halt this run and return to skf-brief-skill to refine scope
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
Steps 4 and 5 are the decision protocol for a scope candidate, which §1c follows too, with the differences it names. Every amendment they append goes to `brief.scope.amendments[]` with `path: candidate.path`, `heuristic: {basename}`, `date: {today ISO}` and `workflow: "skf-update-skill"`, and every brief write goes to `{brief_path}` (the run folder's copy in a read-only mode), preserving all other fields.
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
4. **GATE [default: defer]**: in headless mode (`{headless_mode}` is true), promote nothing and decide nothing: whether a path belongs in scope needs a person. Leave every candidate out of this run and record it as deferred, so the next interactive run asks: append an amendment `action: "deferred-headless"`, `reason: "headless: no user to prompt; left for the next interactive run"`, except for a candidate whose `prior_action` is `deferred-headless`, which a headless run already recorded: write no second amendment for it. A non-interactive update run must never silently add files to scope, and never records a skip no person chose. **Also record one decision per candidate path**, from `{project-root}`:
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
5. Display: `"Promoted {path} — brief amended, scheduled as new file_entries row for file_type doc."`
|
|
159
|
+
```bash
|
|
160
|
+
uv run {emitEnvelopeHelper} record --workflow skf-update-skill --run-dir "{run_dir}" --decision <<'SKF_JSON'
|
|
161
|
+
{"gate": "detect-changes.promoted-doc-prompt", "default_action": "S", "taken_action": "deferred-headless", "reason": "headless: no user to prompt; left for the next interactive run", "evidence": {"path": "<candidate.path>"}}
|
|
162
|
+
SKF_JSON
|
|
163
|
+
```
|
|
165
164
|
|
|
166
|
-
|
|
167
|
-
1. Do NOT modify `scope.include`.
|
|
168
|
-
2. Append a `brief.scope.amendments[]` entry: `action: "skipped"`, `path: candidate.path`, `reason: {user-provided or auto: "user declined promotion at update-skill §1b"}`, `heuristic: {basename}`, `date: {today ISO}`, `workflow: "skf-update-skill"`.
|
|
169
|
-
3. **Write the amended brief back to disk** so neither update-skill nor create-skill will re-prompt in future runs.
|
|
170
|
-
4. Display: `"Skipped {path} — decision recorded in amendments."`
|
|
165
|
+
5. **Apply decision:**
|
|
171
166
|
|
|
172
|
-
- **[
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
3. Exit with status `halted-for-brief-refinement`. Change manifest is discarded — no partial writes.
|
|
167
|
+
- **[P] Promote:** append `candidate.path` to `brief.scope.include` as a literal glob, append an amendment `action: "promoted"`, `reason: {user-provided or auto: "discovered post-creation: matched heuristic {basename}"}`, and **write the amended brief back to disk immediately**. Then append the candidate's `{path, heuristic, size_bytes, line_count, content_hash}`, as the helper reported them, to `promoted_docs_new[]`, which this section keeps as the JSON array `{run_dir}/promoted-docs.json` (each record as the helper printed it; `[]` when none): step 5's `apply` writes a new `file_entries[]` row for each (merge Priority 7). Promoted docs do NOT go through step 3's code re-extraction, which would produce ghost entries on non-code files. Display: `"Promoted {path}: brief amended, scheduled as new file_entries row for file_type doc."`
|
|
168
|
+
- **[S] Skip:** leave `scope.include` as it is, append an amendment `action: "skipped"`, `reason: {user-provided or auto: "user declined promotion at update-skill §1b"}`, and write the amended brief back to disk, so neither update-skill nor create-skill re-prompts in a later run. Display: `"Skipped {path}: decision recorded in amendments."`
|
|
169
|
+
- **[U] Update:** HALT with status `halted-for-brief-refinement` (halt procedure: `phase: "detect-changes:authoritative-files"`, `path: "{brief_path}"`, `reason: "the user chose to refine the brief's scope for {path}"`) and display `"Halting update-skill. Re-run skf-brief-skill to refine scope for {skill_name}, then re-run skf-update-skill."`: no partial writes.
|
|
176
170
|
|
|
177
|
-
|
|
171
|
+
6. **Summary.** After all candidates are resolved (or none were found):
|
|
178
172
|
|
|
179
|
-
- `"Authoritative files mirror: {N} candidates, {P} promoted, {S} skipped, {A}
|
|
173
|
+
- `"Authoritative files mirror: {N} candidates, {P} promoted, {S} skipped, {D} deferred to an interactive run, {A} decided by the brief (in scope or amended), {T} already tracked in provenance."`, with N the helper's `summary.candidates_total`, A its `already_in_scope_count` plus `pre_decided_count` and T its `already_tracked_count`.
|
|
180
174
|
- If N = 0: `"Authoritative files mirror: no candidates."`
|
|
181
175
|
|
|
182
|
-
**Record for evidence report:** the
|
|
183
|
-
|
|
184
|
-
**Interaction with §2 change detection:** promoted docs live in `promoted_docs_new[]`, not in the change manifest. §2.0's `change_detection_excludes` set (built below) is what stops §2 Category A from re-classifying them as ADDED — it is the only coordination that survives across the parallel Category A subprocesses.
|
|
176
|
+
**Record for evidence report:** append the record `authoritative_files_mirror: {candidates: N, promoted: P, skipped: S, deferred: D, pre_decided: A, already_tracked: T, decisions: [{path, action, heuristic, reason}]}` to `{run_dir}/evidence-records.jsonl` as one JSON line keyed `authoritative_files_mirror`: step 5 §4 reads it there, never from context.
|
|
185
177
|
|
|
186
178
|
### 1c. Major-Version Scope Reconciliation (Pre-Detection)
|
|
187
179
|
|
|
@@ -189,8 +181,8 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
189
181
|
|
|
190
182
|
**Skip this section entirely if:**
|
|
191
183
|
|
|
192
|
-
- `update_mode == "gap-driven"` (test-report mode — source hasn't drifted), OR
|
|
193
184
|
- `metadata.json.source_type == "docs-only"` (no source tree to scope), OR
|
|
185
|
+
- `{brief_path}` does not exist (a skill built without a brief, such as a quick skill): there is no scope to expand and no brief to amend. Display `"Scope reconciliation: skipped (no skill brief)."`, OR
|
|
194
186
|
- No audit drift report is available at the path computed in step 1 below.
|
|
195
187
|
|
|
196
188
|
**Procedure:**
|
|
@@ -199,12 +191,14 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
199
191
|
|
|
200
192
|
```bash
|
|
201
193
|
uv run {provenanceGapDispatchHelper} dispatch \
|
|
202
|
-
--skill-name {skill_name} \
|
|
203
|
-
--baseline-version {baseline_version} \
|
|
204
|
-
--forge-data-folder {forge_data_folder} \
|
|
205
|
-
--brief {
|
|
194
|
+
--skill-name "{skill_name}" \
|
|
195
|
+
--baseline-version "{baseline_version}" \
|
|
196
|
+
--forge-data-folder "{forge_data_folder}" \
|
|
197
|
+
--brief "{brief_path}"
|
|
206
198
|
```
|
|
207
199
|
|
|
200
|
+
On exit 1 (a forge folder it cannot read, or a brief that exists but cannot be read), no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "detect-changes:scope-reconciliation"`, `path: "{brief_path}"`, `reason: "<its stderr>"`).
|
|
201
|
+
|
|
208
202
|
Output envelope:
|
|
209
203
|
|
|
210
204
|
```json
|
|
@@ -220,7 +214,7 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
220
214
|
| "pre-decided-demoted" | "unresolved",
|
|
221
215
|
"prior_action": "promoted" | "skipped"
|
|
222
216
|
| "demoted-include" | "demoted-exclude"
|
|
223
|
-
| null
|
|
217
|
+
| "deferred-headless" | null
|
|
224
218
|
}, ...
|
|
225
219
|
],
|
|
226
220
|
"summary": {"pre_decided_count": N, "unresolved_count": N}
|
|
@@ -235,7 +229,7 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
235
229
|
- `status: "already-in-scope"` → skip silently (`prior_action: "promoted"`).
|
|
236
230
|
- `status: "pre-decided-skipped"` → honor silently (`prior_action: "skipped"`).
|
|
237
231
|
- `status: "pre-decided-demoted"` → record as `pre_decided`; do not re-prompt (`prior_action` ∈ `demoted-include`, `demoted-exclude`).
|
|
238
|
-
- `status: "unresolved"` → continue to step
|
|
232
|
+
- `status: "unresolved"` → continue to step 3 (user prompt). A `prior_action` of `deferred-headless` marks a path a headless run deferred (the legacy `skipped` amendment with reason `headless: no user to prompt` reads the same): a person decides it now.
|
|
239
233
|
|
|
240
234
|
**Note:** the Out-of-Scope section is an optional audit-skill output and is often absent or empty — new-file detection is update-skill's job (§1b/§2.2), not audit-skill's — so the `no-report` / `no-candidates` paths above are the common case.
|
|
241
235
|
|
|
@@ -255,95 +249,112 @@ Read the source directory at `{source_root}` and build a current file inventory:
|
|
|
255
249
|
[U] Update — halt this run and return to skf-brief-skill to refine scope
|
|
256
250
|
```
|
|
257
251
|
|
|
258
|
-
4. **
|
|
259
|
-
|
|
260
|
-
5. **Apply decision:**
|
|
261
|
-
|
|
262
|
-
- **[P] Promote:**
|
|
263
|
-
1. Append `candidate.path` to `brief.scope.include` as a literal glob (preserve any wildcards from the drift report).
|
|
264
|
-
2. Append a `brief.scope.amendments[]` entry: `action: "promoted"`, `category: "scope-expansion"`, `path: candidate.path`, `reason: {user-provided or auto: "out-of-scope new public API — drift report {report basename}"}`, `evidence: {evidence string}`, `date: {today ISO}`, `workflow: "skf-update-skill"`.
|
|
265
|
-
3. **Write the amended brief back to disk immediately** at `{forge_data_folder}/{skill_name}/skill-brief.yaml`. Preserve all other fields.
|
|
266
|
-
4. Display: `"Promoted {path} — brief amended; §2 Category A will pick up matching files as ADDED."`
|
|
267
|
-
5. **No `promoted_docs_new[]` entry and no `change_detection_excludes` write** — promoted code globs flow through the standard §2 Category A → §3 extraction path, unlike §1b's promoted docs which bypass extraction.
|
|
252
|
+
4. **GATE [default: defer]**: in headless mode (`{headless_mode}` is true), defer each candidate as §1b step 4 does, with this section's amendment fields (step 5) and the gate `detect-changes.scope-expansion` in its decision record: a non-interactive update run must never silently expand scope.
|
|
268
253
|
|
|
269
|
-
|
|
270
|
-
1. Do NOT modify `scope.include` or `scope.exclude`.
|
|
271
|
-
2. Append a `brief.scope.amendments[]` entry: `action: "skipped"`, `category: "scope-expansion"`, `path: candidate.path`, `reason: {user-provided or auto: "user declined promotion at update-skill §1c"}`, `evidence: {evidence string}`, `date: {today ISO}`, `workflow: "skf-update-skill"`.
|
|
272
|
-
3. **Write the amended brief back to disk** so neither §1c nor a future run will re-prompt.
|
|
273
|
-
4. Display: `"Skipped {path} — decision recorded in amendments."`
|
|
254
|
+
5. **Apply decision** as §1b step 5 does, with these differences:
|
|
274
255
|
|
|
275
|
-
- **[
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
3. Exit with status `halted-for-brief-refinement`. Change manifest is not yet built — no partial writes to provenance.
|
|
256
|
+
- **Amendment fields:** every amendment carries `category: "scope-expansion"` and the candidate's `evidence: {evidence string}` in place of `heuristic`. [P]'s auto reason is "out-of-scope new public API: drift report {report basename}", and [S]'s "user declined promotion at update-skill §1c".
|
|
257
|
+
- **Promotion target:** [P] appends the path to `scope.include` as the drift report gives it, wildcards kept, and adds no `promoted_docs_new[]` entry and no `--exclude`: §2 Category A lists the files a promoted code glob matches as ADDED, and step 3 extracts them, unlike §1b's documents. Display: `"Promoted {path}: brief amended; §2 Category A will pick up matching files as ADDED."`
|
|
258
|
+
- **[S]** leaves `scope.exclude` as it is too, and **[U]** halts with `phase: "detect-changes:scope-reconciliation"`.
|
|
279
259
|
|
|
280
260
|
6. **Summary:** After all candidates are resolved (or none were found):
|
|
281
261
|
|
|
282
|
-
- `"Scope reconciliation: {N} candidates, {P} promoted, {S} skipped, {A} pre-decided from amendments."`
|
|
262
|
+
- `"Scope reconciliation: {N} candidates, {P} promoted, {S} skipped, {D} deferred to an interactive run, {A} pre-decided from amendments."`
|
|
283
263
|
- If N = 0 (section absent or empty): `"Scope reconciliation: no out-of-scope observations in drift report."`
|
|
284
|
-
- If §1c was skipped entirely (no drift report): omit this line; §2.2 will still run.
|
|
264
|
+
- If §1c was skipped entirely (a docs-only skill, no skill brief or no drift report): omit this line; §2.2 will still run.
|
|
285
265
|
|
|
286
|
-
**Record for evidence report:** the
|
|
266
|
+
**Record for evidence report:** append the record `scope_reconciliation_pre: {drift_report: path, candidates: N, promoted: P, skipped: S, deferred: D, pre_decided: A, decisions: [{path, action, evidence}]}` to `{run_dir}/evidence-records.jsonl` as one JSON line keyed `scope_reconciliation_pre` (none when §1c was skipped).
|
|
287
267
|
|
|
288
268
|
### 2. Compare Against Provenance Map
|
|
289
269
|
|
|
290
|
-
**
|
|
270
|
+
**In degraded mode (no provenance map),** run Category A without `--provenance-map`: its `full` mode lists every in-scope file as MODIFIED (with no brief, `--language` scopes the walk), so step 3 re-extracts every export. Run Category B's steps 1 and 2 only (re-extract.md §4's records come from them), and skip its step 3, Category C, Category D and §2.2: there is no map to compare against.
|
|
291
271
|
|
|
292
|
-
#### 2.0
|
|
272
|
+
#### 2.0 Change-Detection Excludes
|
|
293
273
|
|
|
294
|
-
|
|
274
|
+
`promoted_docs_new[]` (§1b) holds documents that reach the provenance map as `file_entries[]` rows through step 4 Priority 7, never through code extraction. Category A leaves each of them out with `--exclude`: each is in scope and the map does not name it yet, so it would read as ADDED, and step 3 would send it to AST extraction, producing ghost entries. A path a `file_entries[]` row already names (a tracked document, script or asset) needs no exclude: Category A never lists one, and Category D compares its hash.
|
|
295
275
|
|
|
296
|
-
|
|
276
|
+
#### 2.1 Categories A, B and C, in Order
|
|
297
277
|
|
|
298
|
-
|
|
299
|
-
- Every source path in `file_entries[].source_file` where `file_type == "doc"` in the existing provenance map. These are already-tracked authoritative docs; any drift in them is handled by Category D (script/asset file changes), not Category A.
|
|
278
|
+
Category B reads the files Category A lists, and Category C pairs what A and B leave deleted and added. Category D (below) needs neither.
|
|
300
279
|
|
|
301
|
-
|
|
280
|
+
**Category A: file-level changes.** Resolve `{classifyChangedFilesHelper}` ← first existing path in `{classifyChangedFilesProbeOrder}` and, from `{project-root}`, run:
|
|
302
281
|
|
|
303
|
-
|
|
282
|
+
```bash
|
|
283
|
+
uv run {classifyChangedFilesHelper} classify \
|
|
284
|
+
--source-root "{source_root}" \
|
|
285
|
+
[--provenance-map "{provenance_map_path}"] \
|
|
286
|
+
[--brief "{brief_path}"] \
|
|
287
|
+
[--language "{language}"] \
|
|
288
|
+
--tree-status "{source_tree_status}" \
|
|
289
|
+
--diff-status "{source_diff_status}" \
|
|
290
|
+
--changed-files "{source_changed_files}" \
|
|
291
|
+
[--exclude "<promoted document path>"] \
|
|
292
|
+
--lists-dir "{run_dir}" \
|
|
293
|
+
> "{run_dir}/category-a.json"
|
|
294
|
+
```
|
|
304
295
|
|
|
305
|
-
|
|
296
|
+
Pass `--provenance-map` whenever init.md §4 found one; `--brief` (as §1c left it) when that file exists (a skill built without a brief, such as a quick skill after its degraded update, has none); `--language` when metadata.json records one; one `--exclude` for each `promoted_docs_new[]` path; and the three init.md §6b values as bound (an empty or `null` value counts as not given). In a tree it takes the changed files from git's list at `{source_changed_files}`, never from file times or sizes, which a checkout rewrites. Add each of its `warnings[]` to `warnings[]`. It writes `{run_dir}/modified-files.json` (the files Category B diffs) and `{run_dir}/extract-files.json` (the modified and added files and each moved file's new path, which Category B and step 3 read): never retype or re-derive a list. On exit 1 (an input it cannot read, named on stderr), no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "detect-changes:category-a"`, the helper's message as `reason`).
|
|
306
297
|
|
|
307
|
-
**Category
|
|
308
|
-
- Files in provenance map but missing from source → DELETED
|
|
309
|
-
- Files in source but not in provenance map AND not in `change_detection_excludes` → ADDED
|
|
310
|
-
- Files in `change_detection_excludes`: skip entirely (routed to file_entries via §1b → step 4 Priority 7, never through Category A)
|
|
311
|
-
- Files in both but with different timestamps/sizes → MODIFIED
|
|
312
|
-
- Files with same content at different paths → MOVED
|
|
298
|
+
**Category B: export-level changes.** Skip it when `{run_dir}/extract-files.json` lists no file.
|
|
313
299
|
|
|
314
|
-
**
|
|
315
|
-
- For each modified file, compare export list against provenance map exports
|
|
316
|
-
- Exports in provenance but not in source → DELETED_EXPORT
|
|
317
|
-
- Exports in source but not in provenance → NEW_EXPORT
|
|
318
|
-
- Exports with changed signatures/types → MODIFIED_EXPORT
|
|
319
|
-
- Exports at different line numbers but same content → MOVED_EXPORT
|
|
300
|
+
1. **The recipe runner.** At Forge tier and above, resolve `{extractPublicApiHelper}` ← first existing path in `{extractPublicApiProbeOrder}` and, from `{project-root}`, run:
|
|
320
301
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
-
|
|
302
|
+
```bash
|
|
303
|
+
uv run {extractPublicApiHelper} --mode full \
|
|
304
|
+
--source-root "{source_root}" \
|
|
305
|
+
--files-from "{run_dir}/extract-files.json" \
|
|
306
|
+
[--language "{language}"] \
|
|
307
|
+
[--scope-type "{scope_type}"] \
|
|
308
|
+
--head-cap 0 \
|
|
309
|
+
-o "{run_dir}/extraction.json"
|
|
310
|
+
```
|
|
324
311
|
|
|
325
|
-
|
|
312
|
+
Pass `--language` and `--scope-type` when metadata.json records them. It takes no `--brief`: Category A applied the scope, and a tracked file the brief's scope leaves out (gap-driven.md §1's rule R1 excludes a rescoped export's file) keeps its exports. `--head-cap 0` keeps every match, since a dropped one would read as a deleted export. Step 3 reads this file and never runs the runner again. **Exit 1** (`incomplete`): run it once more; when still incomplete, step 2 below reads by eye each listed file with no export in `exports[]`. **Exit 2 or 3, no JSON, no candidate resolves, or Quick tier:** write `{"exports": []}` to `{run_dir}/extraction.json`; step 2 reads every listed file by eye (at Quick tier, by text pattern). Tell the user which files were read by eye.
|
|
326
313
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
{
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
314
|
+
2. **What the recipes do not record.** Workers, in parallel batches (Pattern 4) when the list is long, read the modified and added files and each return ONLY `{"exports": [...]}`, with no prose and no fences. For each function export the runner found whose `params` it left null (it could not read the signature): `export_name` and `source_file` as it wrote them, with `params` (each parameter as the source writes it, `name: type`, the form the provenance map's `params` hold) and `return_type` (null when there is none), read at its `source_line`. For each export it could not find (a name `entry_point_diff.extraction_gaps[]` lists, a form Known Limitation #11 in `{extractionPatternsData}` lists, an export of a file `file_issues[]` names or that was read by eye): the same, plus `export_type`, `source_line`, `confidence: T1-low` and `extraction_method: source-read`. Write the union of their `exports` arrays to `{run_dir}/export-details.json`.
|
|
315
|
+
|
|
316
|
+
3. **The diff.** Resolve `{structuralDiffHelper}` ← first existing path in `{structuralDiffProbeOrder}` and, from `{project-root}`, run:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
uv run {structuralDiffHelper} "{provenance_map_path}" "{run_dir}/extraction.json" \
|
|
320
|
+
--current-extra "{run_dir}/export-details.json" \
|
|
321
|
+
--files "{run_dir}/modified-files.json" \
|
|
322
|
+
-o "{run_dir}/category-b-diff.json"
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Exit 0 (no difference) and exit 1 (differences) both wrote the diff, which §3's helper maps onto Category B: never re-diff by eye. On exit 2, no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "detect-changes:category-b"`, the helper's `error` as `reason`).
|
|
326
|
+
|
|
327
|
+
**Category C: rename detection.** The fixed rules pair Category A's `deleted` files with its `added` ones, and the diff's `removed[]` exports with its `added[]` ones and with the added files' exports, by script, never by eye. From `{project-root}`, run:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
uv run {buildChangeManifestHelper} rename-candidates \
|
|
331
|
+
--category-a "{run_dir}/category-a.json" \
|
|
332
|
+
[--category-b-diff "{run_dir}/category-b-diff.json"] \
|
|
333
|
+
--provenance-map "{provenance_map_path}" \
|
|
334
|
+
[--extraction "{run_dir}/extraction.json"] \
|
|
335
|
+
[--export-details "{run_dir}/export-details.json"] \
|
|
336
|
+
--tier "{forge_tier}" \
|
|
337
|
+
--source-root "{source_root}" \
|
|
338
|
+
[--sizes-commit "{source_commit}"] \
|
|
339
|
+
-o "{run_dir}/category-c.json"
|
|
334
340
|
```
|
|
335
341
|
|
|
336
|
-
|
|
342
|
+
Pass `--category-b-diff`, `--extraction` and `--export-details` when Category B wrote them, and `--sizes-commit` only when `{source_tree_status}` is `ready` or `offline`: in that tree the helper reads a deleted file's size at the pinned commit (`git cat-file -s`), while in a local source the file is gone and the size test is skipped. Skip Category C in degraded mode (there is no map). Content similarity above 80% (fixed, not configurable) is a rename: at Quick tier a file size within 20% and export names that overlap above 70% (export names above 80% alike), at Forge and above equal export signatures. The helper keeps a pair only when it is each side's best match, so a tie pairs nothing, and writes `category_c` (`renamed_files: [{old_path, new_path}]`, `renamed_exports: [{old_name, new_name, file, old_file?}]`, `old_file` only when it differs from `file`), the `evidence` of each pair and what it left `unpaired`. On exit 1, no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "detect-changes:category-c"`, its stderr as `reason`). Category A's `moved_files[]` (same-content moves) are not Category C's to pair, and §3's helper takes each pair out of the lists it was found in.
|
|
343
|
+
|
|
344
|
+
**CCC check (Forge+ and Deep, a local source only).** When `tools.ccc` is true and `{source_tree_status}` is neither `ready` nor `offline` (the tree init.md §6b prepared has no ccc index, and a search there could start indexing a folder step 7 deletes), you may pair what the rules left in `unpaired`: a deleted file with an added one that CCC ranks as the same code (`ccc_bridge.search` over the deleted file's export names; **Tool resolution:** `/ccc` skill search, ccc MCP or `ccc search`). This is the one judgment in Category C: add a pair only on CCC's evidence, name it in the report as a CCC pairing, and never undo a pair the rules made. Write only these pairs, with each path as `unpaired` gives it, to `{run_dir}/ccc-pairs.json` as `{"renamed_files": [{"old_path": "<deleted>", "new_path": "<added>"}]}`; the helpers below read `{run_dir}/category-c.json` themselves, so never copy its pairs.
|
|
337
345
|
|
|
338
|
-
**Category D
|
|
346
|
+
**Category D: script, asset and tracked-document file changes.**
|
|
339
347
|
|
|
340
|
-
Run the bulk comparison once
|
|
348
|
+
Run the bulk comparison once, keeping its output in the run folder (step 5's `apply` reads it):
|
|
341
349
|
|
|
342
350
|
```bash
|
|
343
|
-
uv run {hashContentHelper} compare
|
|
344
|
-
--provenance-map
|
|
351
|
+
uv run {hashContentHelper} compare "{source_root}" \
|
|
352
|
+
--provenance-map "{provenance_map_path}" \
|
|
353
|
+
> "{run_dir}/category-d-compare.json"
|
|
345
354
|
```
|
|
346
355
|
|
|
356
|
+
On exit 1, no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "detect-changes:category-d"`, its stderr as `reason`).
|
|
357
|
+
|
|
347
358
|
The helper emits:
|
|
348
359
|
|
|
349
360
|
```json
|
|
@@ -357,43 +368,47 @@ The helper emits:
|
|
|
357
368
|
}
|
|
358
369
|
```
|
|
359
370
|
|
|
360
|
-
Translate the helper's output into the change manifest:
|
|
361
|
-
- `MODIFIED_FILE` rows → add to manifest as MODIFIED_FILE
|
|
362
|
-
- `DELETED_FILE` rows → add to manifest as DELETED_FILE
|
|
363
|
-
- `UNCHANGED` rows → omit from the manifest (no action needed)
|
|
364
|
-
|
|
365
371
|
The compare helper reports only tracked files; NEW_FILE detection (a file present in source but absent from the provenance map) is a set-difference, so it runs through a script rather than the prompt. Pipe the same deterministic detector create-skill step 3 §4c uses (resolved via `detectScriptsAssetsProbeOrder`) into `{newFileDiffHelper}`, which subtracts the provenance map's `file_entries[].source_file` and sets aside user-authored `[MANUAL]` paths:
|
|
366
372
|
|
|
367
373
|
```bash
|
|
368
|
-
uv run {detectScriptsAssetsHelper} detect
|
|
369
|
-
| uv run {newFileDiffHelper} {
|
|
374
|
+
uv run {detectScriptsAssetsHelper} detect "{source_root}" \
|
|
375
|
+
| uv run {newFileDiffHelper} "{provenance_map_path}" \
|
|
376
|
+
> "{run_dir}/new-files.json"
|
|
370
377
|
```
|
|
371
378
|
|
|
372
|
-
It
|
|
379
|
+
It writes `{"new_files":[{source_file, kind}], "skipped_manual":[...], "already_tracked":[...], "stats":{...}}`. §3's `build` reads both files, typing each compare row by its `file_entries[]` row's `file_type` and each new file by its `kind`: sort no row by hand. `skipped_manual[]` are user-authored files under `scripts/[MANUAL]/` or `assets/[MANUAL]/`, preserved and not touched; `already_tracked[]` were handled by the compare above.
|
|
373
380
|
|
|
374
|
-
|
|
381
|
+
**Write the category JSON** to `{run_dir}/categories.json`, where §2.2 and §3 read it beside the helper files: the two flags, `degraded_mode` and `update_mode: "normal"`. Category C stays in the files its steps wrote, which §2.2, §3 and step 5's `apply` take as `--category-c` and `--ccc-pairs`:
|
|
375
382
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
383
|
+
```bash
|
|
384
|
+
cat > "{run_dir}/categories.json" <<'SKF_JSON'
|
|
385
|
+
{"degraded_mode": <bool>, "update_mode": "normal"}
|
|
386
|
+
SKF_JSON
|
|
387
|
+
```
|
|
380
388
|
|
|
381
389
|
#### 2.2 — Major-Version Scope Reconciliation (Post-Detection)
|
|
382
390
|
|
|
383
391
|
**Purpose:** §1c catches the major-version case when an audit drift report supplies explicit candidates. §2.2 is the safety net that fires when no audit was run (or audit emitted no out-of-scope section): it inspects the just-built Category A/B results for the deletion-ratio signature of a major-version restructure and gives the user an off-ramp before §3 commits the change manifest.
|
|
384
392
|
|
|
385
|
-
**Trigger computation:** invoke the helper with the
|
|
393
|
+
**Trigger computation:** skip §2.2 in degraded mode (there is no map). Otherwise invoke the helper with the files §3 reads, plus the provenance map:
|
|
386
394
|
|
|
387
395
|
```bash
|
|
388
|
-
|
|
389
|
-
--provenance-map {
|
|
396
|
+
uv run {buildChangeManifestHelper} deletion-ratio \
|
|
397
|
+
--provenance-map "{provenance_map_path}" \
|
|
398
|
+
--category-a "{run_dir}/category-a.json" \
|
|
399
|
+
[--category-b-diff "{run_dir}/category-b-diff.json"] \
|
|
400
|
+
[--category-c "{run_dir}/category-c.json"] \
|
|
401
|
+
[--ccc-pairs "{run_dir}/ccc-pairs.json"] \
|
|
402
|
+
--input "{run_dir}/categories.json"
|
|
390
403
|
```
|
|
391
404
|
|
|
392
|
-
|
|
405
|
+
Pass `--category-b-diff`, `--category-c` and `--ccc-pairs` when Category B, Category C and the CCC check wrote them.
|
|
406
|
+
|
|
407
|
+
The helper handles the two skip conditions internally: when the input has `degraded_mode: true`, or the provenance has zero entries, it returns `skip_reason` set and `should_trigger: false`. `should_trigger` fires when the deletion ratio reaches the fixed bundled threshold of 50% (`ratio >= 0.50`, baked into the helper, not configurable). The output envelope:
|
|
393
408
|
|
|
394
409
|
```json
|
|
395
410
|
{
|
|
396
|
-
"skip_reason": "
|
|
411
|
+
"skip_reason": "degraded-mode" | "zero-provenance-exports" | null,
|
|
397
412
|
"deleted_export_count": N,
|
|
398
413
|
"total_provenance_exports": N,
|
|
399
414
|
"deletion_ratio": 0.X,
|
|
@@ -424,37 +439,37 @@ The upstream surface appears to have been substantially replaced. The brief's
|
|
|
424
439
|
[A] Audit — halt and run skf-audit-skill to map the new surface, then re-run update-skill
|
|
425
440
|
```
|
|
426
441
|
|
|
427
|
-
**
|
|
442
|
+
**GATE [default: C]**: in headless mode (`{headless_mode}` is true), auto-select `[C] Continue`, append the record `scope_reconciliation_post: {trigger: "deletion-ratio", ratio: X, decision: "headless-continue"}` to `{run_dir}/evidence-records.jsonl` as one JSON line keyed `scope_reconciliation_post` (step 5 §4 lists it in the evidence report), and surface the warning in step 6's report. A non-interactive run must not silently halt, but the user must be able to see the signal post-hoc. **Also record the decision**, from `{project-root}`:
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
uv run {emitEnvelopeHelper} record --workflow skf-update-skill --run-dir "{run_dir}" --decision <<'SKF_JSON'
|
|
446
|
+
{"gate": "detect-changes.deletion-ratio", "default_action": "C", "taken_action": "C", "reason": "headless: deletion-ratio threshold exceeded but no user to halt", "evidence": {"deletion_ratio": <ratio>, "deleted_export_count": <N>, "total_provenance_exports": <T>}}
|
|
447
|
+
SKF_JSON
|
|
448
|
+
```
|
|
428
449
|
|
|
429
450
|
**Apply decision:**
|
|
430
451
|
|
|
431
|
-
- **[C] Continue:** record `scope_reconciliation_post: {trigger: "deletion-ratio", ratio: X, decision: "continue"}` and proceed to §3.
|
|
432
|
-
- **[B] Brief:**
|
|
433
|
-
- **[A] Audit:**
|
|
452
|
+
- **[C] Continue:** unless the headless branch above appended its record, append `scope_reconciliation_post: {trigger: "deletion-ratio", ratio: X, decision: "continue"}` to `{run_dir}/evidence-records.jsonl` the same way, and proceed to §3.
|
|
453
|
+
- **[B] Brief:** HALT with status `halted-for-brief-refinement` (halt procedure: `phase: "detect-changes:deletion-ratio"`, `reason: "the user chose to refine the brief's scope: {deleted_export_count} of {total_provenance_exports} exports deleted"`). Display: `"Halting update-skill. Re-run skf-brief-skill to refine scope for {skill_name}, then re-run skf-update-skill."` The change manifest is discarded: no partial writes.
|
|
454
|
+
- **[A] Audit:** HALT with status `halted-for-audit` (halt procedure: `phase: "detect-changes:deletion-ratio"`, `reason: "the user chose to audit the new surface first"`). Display: `"Halting update-skill. Run skf-audit-skill against {skill_name} to map the new surface: its drift report will feed §1c on the next update-skill run."` The change manifest is discarded.
|
|
434
455
|
|
|
435
456
|
### 3. Build Change Manifest
|
|
436
457
|
|
|
437
|
-
Hand the
|
|
458
|
+
Hand the helper files and the category JSON to the helper, which takes Category A from the classify output, maps the diff onto Category B, and takes each Category C pair out of the lists it was found in:
|
|
438
459
|
|
|
439
460
|
```bash
|
|
440
|
-
|
|
461
|
+
uv run {buildChangeManifestHelper} build \
|
|
462
|
+
[--category-a "{run_dir}/category-a.json"] \
|
|
463
|
+
[--category-b-diff "{run_dir}/category-b-diff.json"] \
|
|
464
|
+
[--category-c "{run_dir}/category-c.json"] \
|
|
465
|
+
[--ccc-pairs "{run_dir}/ccc-pairs.json"] \
|
|
466
|
+
[--file-compare "{run_dir}/category-d-compare.json" --provenance-map "{provenance_map_path}"] \
|
|
467
|
+
[--new-files "{run_dir}/new-files.json"] \
|
|
468
|
+
--input "{run_dir}/categories.json" \
|
|
469
|
+
> "{run_dir}/change-manifest.json"
|
|
441
470
|
```
|
|
442
471
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
```json
|
|
446
|
-
{
|
|
447
|
-
"category_a": {"modified": [...], "added": [...], "deleted": [...]},
|
|
448
|
-
"category_b": {"modified_exports": [...], "new_exports": [...],
|
|
449
|
-
"deleted_exports": [...], "moved_exports": [...]},
|
|
450
|
-
"category_c": {"renamed_files": [...], "renamed_exports": [...]},
|
|
451
|
-
"category_d": {"scripts_modified": [...], "scripts_added": [...],
|
|
452
|
-
"scripts_deleted": [...], "assets_modified": [...],
|
|
453
|
-
"assets_added": [...], "assets_deleted": [...]},
|
|
454
|
-
"degraded_mode": <bool>,
|
|
455
|
-
"update_mode": "normal" | "gap-driven"
|
|
456
|
-
}
|
|
457
|
-
```
|
|
472
|
+
Pass each helper file its category wrote. On exit 1 or no JSON: HALT with status `blocked` (halt procedure: `phase: "detect-changes:change-manifest"`, its stderr as `reason`).
|
|
458
473
|
|
|
459
474
|
The helper emits the unified manifest envelope:
|
|
460
475
|
|
|
@@ -467,21 +482,23 @@ The helper emits the unified manifest envelope:
|
|
|
467
482
|
"exports_modified": N, "exports_new": N, "exports_deleted": N,
|
|
468
483
|
"exports_renamed": N, "exports_moved": N,
|
|
469
484
|
"scripts_modified": N, "scripts_added": N, "scripts_deleted": N,
|
|
470
|
-
"assets_modified": N, "assets_added": N, "assets_deleted": N
|
|
485
|
+
"assets_modified": N, "assets_added": N, "assets_deleted": N,
|
|
486
|
+
"docs_modified": N, "docs_deleted": N
|
|
471
487
|
},
|
|
472
488
|
"total_export_changes": N,
|
|
473
489
|
"per_file": [
|
|
474
490
|
{"file_path": "...", "status": "MODIFIED|ADDED|DELETED|MOVED",
|
|
475
491
|
"exports_affected": [{name, change_type, old_line, new_line}, ...]}
|
|
476
|
-
]
|
|
492
|
+
],
|
|
493
|
+
"category_d": {"scripts_modified": [...], ..., "docs_deleted": [...]}
|
|
477
494
|
}
|
|
478
495
|
```
|
|
479
496
|
|
|
480
|
-
`per_file` entries are sorted MODIFIED → ADDED → DELETED → MOVED, then alphabetically within each status group, so downstream stages can rely on stable ordering. MOVED entries include an extra `old_path` field.
|
|
497
|
+
`per_file` entries are sorted MODIFIED → ADDED → DELETED → MOVED, then alphabetically within each status group, so downstream stages can rely on stable ordering. MOVED entries include an extra `old_path` field. `category_d` lists the paths behind the `scripts_*`, `assets_*` and `docs_*` counts: a `docs_*` path is a tracked document, which only Category D detects, so a change to one alone is a change. `{run_dir}/change-manifest.json` is the change manifest: step 3, step 6 and step 5's `apply` read it there, so it survives a compacted context.
|
|
481
498
|
|
|
482
499
|
### 4. Check for No-Change Shortcut
|
|
483
500
|
|
|
484
|
-
**If zero changes detected across all categories
|
|
501
|
+
**If zero changes detected across all categories** (`no_changes` is true in `{run_dir}/change-manifest.json`; for a docs-only skill, when every hashed document matched):
|
|
485
502
|
|
|
486
503
|
"**No changes detected.** Source code matches provenance map exactly.
|
|
487
504
|
|
|
@@ -489,23 +506,17 @@ The skill `{skill_name}` is current — no update needed.
|
|
|
489
506
|
|
|
490
507
|
**Skipping to report step...**"
|
|
491
508
|
|
|
492
|
-
→ Skip steps
|
|
509
|
+
→ Skip steps 3-5, immediately load {noChangeReportFile} with "no changes" status. A `--detect-only` or `--dry-run` run takes this route too and stays read-only there: report.md §1 prints its line with no result file and never runs `{onCompleteCommand}`.
|
|
493
510
|
|
|
494
511
|
### 5. Display Change Summary and Route
|
|
495
512
|
|
|
496
|
-
"**
|
|
513
|
+
Display one line from the change manifest: "**Detected:** {modified} modified, {added} added, {deleted} deleted and {moved} moved or renamed files; {total_export_changes} exports affected." The report (step 6) shows the full counts.
|
|
497
514
|
|
|
498
|
-
|
|
499
|
-
|----------|-------|
|
|
500
|
-
| Files modified | {count} |
|
|
501
|
-
| Files added | {count} |
|
|
502
|
-
| Files deleted | {count} |
|
|
503
|
-
| Files moved/renamed | {count} |
|
|
504
|
-
| Exports affected | {total_export_changes} |"
|
|
515
|
+
For a docs-only skill, show instead the documents that changed (`changed_urls`) and the ones that could not be fetched.
|
|
505
516
|
|
|
506
517
|
This step auto-proceeds — no user choices. Once the change manifest is fully built, load and fully read the next file, then execute it, per the branch that applies:
|
|
507
518
|
|
|
508
|
-
- **`detect_only_mode == true`** → display "**Detect-only mode
|
|
519
|
+
- **`detect_only_mode == true`** → display "**Detect-only mode: skipping re-extract, merge and write.** Loading report..." and load `{noChangeReportFile}` (report.md), which emits status `detect-only`. Do not load `{nextStepFile}`.
|
|
509
520
|
- **No changes detected** (section 4) → load `{noChangeReportFile}` (report.md), which emits status `no-changes`.
|
|
510
|
-
- **Otherwise** → display "**Proceeding to re-extraction of {affected_file_count
|
|
521
|
+
- **Otherwise** → display "**Proceeding to re-extraction of {affected_file_count} changes...**" and load `{nextStepFile}` (re-extract.md) to begin re-extraction.
|
|
511
522
|
|