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
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
---
|
|
2
|
+
# `{nextStepFile}` is where a repair goes on, step 4 (§5 loads it).
|
|
3
|
+
# `{reportFile}` takes a detect-only run, a run with no gap to repair (§2)
|
|
4
|
+
# and a dry run (§5).
|
|
5
|
+
nextStepFile: 'merge.md'
|
|
6
|
+
reportFile: 'report.md'
|
|
7
|
+
extractionPatternsData: 'skf-create-skill/references/extraction-patterns.md'
|
|
8
|
+
tierDegradationRulesData: 'skf-create-skill/references/tier-degradation-rules.md'
|
|
9
|
+
# `{parseGapsHelper}`: §1 reads the test report's gaps. HALT if neither exists.
|
|
10
|
+
parseGapsProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-parse-gaps.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-parse-gaps.py'
|
|
13
|
+
# Resolve `{checkWorkspaceDriftHelper}` to the first existing path; HALT if
|
|
14
|
+
# neither candidate exists. §3 relies on the helper for the deterministic
|
|
15
|
+
# workspace-pinning guard (git rev-parse + short-SHA prefix match + halt
|
|
16
|
+
# message rendering). Falling back to prose-driven git invocation would
|
|
17
|
+
# lose the four-state dispatch (ok / skipped / mismatch / overridden).
|
|
18
|
+
checkWorkspaceDriftProbeOrder:
|
|
19
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-check-workspace-drift.py'
|
|
20
|
+
- '{project-root}/src/shared/scripts/skf-check-workspace-drift.py'
|
|
21
|
+
# `{extractPublicApiHelper}`: §4a's recipe runner. If neither exists, read
|
|
22
|
+
# the files by eye.
|
|
23
|
+
extractPublicApiProbeOrder:
|
|
24
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
|
|
25
|
+
- '{project-root}/src/shared/scripts/skf-extract-public-api.py'
|
|
26
|
+
# `{buildChangeManifestHelper}`: §4's `gap-records` and §4a's `records`.
|
|
27
|
+
# HALT if neither exists.
|
|
28
|
+
buildChangeManifestProbeOrder:
|
|
29
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-build-change-manifest.py'
|
|
30
|
+
- '{project-root}/src/shared/scripts/skf-build-change-manifest.py'
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
<!-- Config: communicate in {communication_language}. -->
|
|
34
|
+
|
|
35
|
+
# Gap-Driven Repair
|
|
36
|
+
|
|
37
|
+
## STEP GOAL:
|
|
38
|
+
|
|
39
|
+
Repair the gaps a test report found, at the commit the skill is pinned to. When `update_mode` is `gap-driven` (set in step 1 by `--from-test-report` or by its §4b offer), init.md §8 loads this file in place of steps 2 and 3: it translates the report's gaps into the change manifest, checks that `{source_root}` holds the pinned commit, spot-checks each gap's export there and re-extracts only the exports the report gives no line for. The source has not drifted, so no file is compared.
|
|
40
|
+
|
|
41
|
+
## Rules
|
|
42
|
+
|
|
43
|
+
- Focus only on the gaps the report lists: merge nothing, and read only the source lines and files §4 and §4a name
|
|
44
|
+
- Write nothing outside `{run_dir}`, in any mode
|
|
45
|
+
|
|
46
|
+
## Steps
|
|
47
|
+
|
|
48
|
+
The helpers below pass their JSON to steps 4 and 5 through `{run_dir}`, the run folder SKILL.md On Activation created.
|
|
49
|
+
|
|
50
|
+
**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. This step writes nothing outside `{run_dir}`, so there is nothing to undo, `--dry-run` included.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uv run {runStateHelper} halt --run-dir "{run_dir}" \
|
|
54
|
+
[--lock "{forge_data_folder}/{skill_name}/.skf-update.lock" --owner "{lock_owner}"] \
|
|
55
|
+
[--emit] <<'SKF_JSON'
|
|
56
|
+
{"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": "gap-driven"}
|
|
57
|
+
SKF_JSON
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Pass `--lock` and `--owner` when init.md §1b bound `{lock_owner}` (the read-only modes take no lock), and `--emit` in `{headless_mode}`. It 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 (`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: "re-extract:<the helper's file name>"` and `reason: "<the helper's file name> is missing; re-install SKF"`.
|
|
61
|
+
|
|
62
|
+
The halt leaves `{run_dir}` in place.
|
|
63
|
+
|
|
64
|
+
**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`.
|
|
65
|
+
|
|
66
|
+
### 1. Translate the Test Report's Gaps
|
|
67
|
+
|
|
68
|
+
Read the gaps of the test report at `{test_report_path}` and translate them into the change manifest (the source has not changed, so no file is compared):
|
|
69
|
+
|
|
70
|
+
1. **Read the gaps through `{parseGapsHelper}`** (resolve it ← first existing path in `{parseGapsProbeOrder}`). From `{project-root}`, run:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uv run {parseGapsHelper} parse \
|
|
74
|
+
--report "{test_report_path}" \
|
|
75
|
+
[--source-root "{source_root}"] \
|
|
76
|
+
--provenance-map "{provenance_map_path}" \
|
|
77
|
+
> "{run_dir}/gaps.json"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Pass `--source-root` unless the skill is docs-only (there is no source tree). It reads the gap ledger test-skill wrote beside the report (`test-findings-<the report's run id>.json`), which holds every gap of a run test-skill's hard gate blocked (its `stepsCompleted` ends at `hard-gate`), and for a report older than the ledger the report's `## Gap Report`. Never read gaps from the report by eye. `--provenance-map` (init.md §4 halts a repair that has none) gives each gap that names an `export` its `map_match`, the map entry §4 spot-checks, keyed on the gap's `source_citation`.
|
|
81
|
+
|
|
82
|
+
- **Exit 0:** bullet 3 reads `{run_dir}/gaps.json` and passes each of its `warnings[]` on as `test-report: <entry>`.
|
|
83
|
+
- **Exit 1** (`status: "error"` in `{run_dir}/gaps.json`, with `code` `REPORT_MISSING`, `LEDGER_MISSING`, `LEDGER_INVALID` or `INVALID_INPUT`), **any other exit, no JSON, or no candidate resolves:** HALT with status `blocked` and display "**The test report's gaps could not be read:** {code}: {error}" ("skf-parse-gaps.py is missing; re-install SKF" when no candidate resolved). The halt procedure takes `phase: "detect-changes:parse-gaps"`, `path: "{test_report_path}"`, `reason: "{code}: {error}"`.
|
|
84
|
+
|
|
85
|
+
2. **Translate each gap by its `category`** (the gap ledger's closed set of slugs), never by its severity: bullet 3's helper routes each one through the table below. A report older than the ledger gives no `category`: when bullet 3 asks, choose the row whose gap type its title and issue describe.
|
|
86
|
+
|
|
87
|
+
| `category` | Gap type (a report older than the ledger) | Change Category |
|
|
88
|
+
|------------|-------------------------------------------|-----------------|
|
|
89
|
+
| `missing-export`, `missing-type` | Missing export, type or interface documentation | NEW_EXPORT (an undocumented public export or type), unless the remediation says the export is internal or out of scope: then DELETED_EXPORT (rescope), see rule R1 |
|
|
90
|
+
| `split-body-mismatch` | Split-body inconsistency: the SKILL.md body and a `references/*.md` file document one export differently, with a `Source:` inside the skill package | STRUCTURAL_FIX, see rule R2 (for an older report, this row takes precedence over the Signature mismatch row below) |
|
|
91
|
+
| `signature-mismatch`, `fabricated-signature`, `stale-documentation` | Signature mismatch; stale documentation (the skill documents an export the source no longer has) | MODIFIED_EXPORT (the documentation needs updating from source) |
|
|
92
|
+
| `structural`, `broken-reference`, `inaccurate-reference`, `reference-escape`, `integration-pattern`, `migration-section`, `scripts-assets` | Structural or coherence drift in the output files only, no source change | STRUCTURAL_FIX, see rule R2 |
|
|
93
|
+
| `provenance-completeness` | Export documented in SKILL.md/references but **missing from the provenance-map** | NEW_EXPORT (provenance-completeness), see rule R3 |
|
|
94
|
+
| `provenance-line` | Provenance line is not the export's definition | MOVED_EXPORT (provenance line), see rule R5 |
|
|
95
|
+
| `metadata-drift`, `metadata` | Divergent export counts in the metadata; missing metadata or examples | metadata update, see rule R4 |
|
|
96
|
+
| any other | any other gap | not routed |
|
|
97
|
+
|
|
98
|
+
**Not routed:** update-skill has no repair for the other categories. `provenance-unverified` asks for a check by hand, `denominator-inflation` and `multi-denominator` concern the brief's scope and the count design, and `numerator-inflation`, `scripts-assets-provenance`, `observation`, `discovery`, `description` and `external-validator` need a person or another workflow. Such a gap takes no manifest entry (bullet 3 lists it).
|
|
99
|
+
|
|
100
|
+
**Translation rules (referenced by the table above):**
|
|
101
|
+
|
|
102
|
+
- **R1: DELETED_EXPORT (rescope).** A coverage gap is a rescope only when its remediation text names removal (`rescope`, `remove from surface`, `out of scope`) or the export is upstream `#[doc(hidden)]` / internal. Default a `missing-export` or `missing-type` gap to NEW_EXPORT (document it); choose rescope only on that explicit signal. Bullet 3 asks `rescope` for each `missing-export` or `missing-type` gap whose remediation a person or a model wrote (any text but the one test-skill's gap ledger generates for it, which names no removal): answer it by this rule, `false` when the item's `remediation` and `issue` give no such signal, and answer a gap it does not ask about only when you found its export upstream `#[doc(hidden)]` or internal (bullet 3's `written`). **Interactive:** prompt per qualifying gap: "[D] Document the export / [R] Rescope (remove from the public surface)", and answer `rescope: true` for [R]. **GATE [default: D]**: headless answers `false` (**document**, NEW_EXPORT); `true` only when the remediation explicitly says removal *and* the export is internal/`#[doc(hidden)]`. Headless, record one decision for each `rescope` you answer, from `{project-root}`:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uv run {emitEnvelopeHelper} record --workflow skf-update-skill --run-dir "{run_dir}" --decision <<'SKF_JSON'
|
|
106
|
+
{"gate": "gap-driven.rescope", "default_action": "D", "taken_action": "<D for false, R for true>", "reason": "headless: <the rule R1 test applied>", "evidence": {"gap_id": "<gap_id>", "export": "<name>", "remediation": "<its text>"}}
|
|
107
|
+
SKF_JSON
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
A rescope is honest only if the reduction is expressed in the brief's scope: on `true`, bullet 3's helper will give the manifest entry a `rescope` object holding the `scope.amendments[]` entry (`category: "scope-expansion"`, `action: "excluded"`, `path`, `reason` (the remediation), `date`, `workflow: "skf-update-skill"`) **and** the export's source path for `brief.scope.exclude`, then route the entry to merge Priority 1 (removal) so steps 4 and 5 remove it and recompute stats from the amended scope. This step writes neither to the brief: step 4 §6b writes both, the first write of the repair, and skips an amendment or an exclude path the brief already holds, so a re-run never adds them twice and a run that stops before step 4 (a halt, `--dry-run`, `--detect-only`) leaves the brief as it was. In those read-only modes its `--read-only` adds `proposed-amendment: excluded {path} (scope-expansion); not written: {--dry-run or --detect-only}` to its `warnings[]`. Never close a coverage gap by editing `metadata.stats` to equal the documented count: that is denominator deflation and `skf-test-skill` will reject it.
|
|
111
|
+
- **R2: STRUCTURAL_FIX.** A finding whose fix edits the generated markdown only, with no source change and no provenance entry change. It carries `remediation` text describing the surgical markdown edit and routes to merge Priority 8 (generated-markdown edit only); it never adds, modifies, or removes a provenance `entries[]` row. Two kinds of finding qualify:
|
|
112
|
+
- 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;
|
|
113
|
+
- a split-body consistency finding (test-skill coverage-check §1b `cross_check_mismatches`): the SKILL.md body and a `references/*.md` file document one export differently. test-skill rates it High, as it rates a signature mismatch against the source, yet it needs nothing from the source: merge Priority 8 edits the `references/*.md` file to match the body. Its `category` is `split-body-mismatch`; in a report older than the ledger, recognize it by its `Source:`, which points inside the skill package (at the skill's own `SKILL.md` or at one of its `references/*.md` files, whether the path is written relative to the package or through `{skill_package}`), while its issue sets the SKILL.md body against a `references/*.md` file. Test for it before the High "Signature mismatch" row: a High signature gap whose `Source:` points into the source tree, or that has no `Source:`, stays `MODIFIED_EXPORT`.
|
|
114
|
+
- **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 §4a's targeted re-extraction **regardless of severity**, gated only on `source_root` being pinned and readable (§4).
|
|
115
|
+
- **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).
|
|
116
|
+
- **R5: MOVED_EXPORT (provenance line).** A `provenance-line` gap (in a report older than the ledger, a Low gap titled `Provenance line is not the definition of {export_name}`, test-skill coverage-check §4c) says the provenance map's `source_line` for that export is not the line that defines it. Its `Source:` is the map's `{source_file}:{source_line}`, so `source_citation` is set, and its remediation lists the file's definition lines. This row takes precedence over the Low `metadata update` row (R4). Route the entry to §4's spot-check only, with no public-reachability gate and no §4a.
|
|
117
|
+
|
|
118
|
+
3. **Build the change manifest through `{parseGapsHelper}`**, never by hand. From `{project-root}`, run:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv run {parseGapsHelper} translate \
|
|
122
|
+
--gaps "{run_dir}/gaps.json" \
|
|
123
|
+
--provenance-map "{provenance_map_path}" \
|
|
124
|
+
[--judgments "{run_dir}/gap-judgments.json"] \
|
|
125
|
+
[--read-only {dry-run|detect-only}] \
|
|
126
|
+
-o "{run_dir}/change-manifest.json"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Pass `--judgments` once the file exists, and `--read-only` with the run's flag in a `--detect-only` or `--dry-run` run. It routes each gap by the table and rules R1 to R5 and writes `{run_dir}/change-manifest.json`, where §4, merge and step 5 read it, as `{"mode": "gap-driven", "entries": [...]}`, each entry with the fields the "Translate" section of its docstring lists: §4 partitions on its `change_category` and merge.md §3 dispatches on it. Act on its `status`:
|
|
130
|
+
|
|
131
|
+
- **`needs-judgment`** (it wrote nothing): for each `needs_judgment[]` item, judge from its gap's `title`, `issue` and `remediation`, which the item carries, add your answer to each of its `needs` to `{run_dir}/gap-judgments.json` under the item's `gap_id` (`{"<gap_id>": {"<need>": <answer>}}`), then run it again:
|
|
132
|
+
- `category` (a report older than the ledger): the slug of the row bullet 2 says the gap's title and issue describe (`missing-export` or `missing-type` for a missing export or type), or null for a gap no row routes;
|
|
133
|
+
- `name`: the export the gap's title names, or null when it names none, which leaves the gap unrouted;
|
|
134
|
+
- `rescope`: rule R1's answer, true or false;
|
|
135
|
+
- `source_file`: the source file, relative to `{source_root}`, that defines the export (a rescope's exclude path) or that a rule R3 gap's documentation cites, or null.
|
|
136
|
+
- **`written`:** set `gap_count` from its `gap_count`, keep its `not_routed[]` (the `id`, `title` and `category` of each gap the table does not route) for §2, and add each of its `warnings[]` to `warnings[]`: the parse's, as `test-report: <entry>`, `test-report: not routed: {id} ({category})` for each gap not routed, and a read-only run's proposed amendments. When you answer `rescope` for a gap it did not ask about (rule R1: an export you found upstream internal or `#[doc(hidden)]`), add the answer to `{run_dir}/gap-judgments.json` and run it again before §2, taking that run's output in place of this one.
|
|
137
|
+
- **Exit 1** (`status: "error"`) **or no JSON:** HALT with status `blocked` and display "**The test report's gaps could not be translated:** {code}: {error}". The halt procedure takes `phase: "detect-changes:parse-gaps"`, `path: "{test_report_path}"`, `reason: "{code}: {error}"`.
|
|
138
|
+
|
|
139
|
+
### 2. Display the Gap Summary and Route
|
|
140
|
+
|
|
141
|
+
Display "**Gap-driven update mode.** Translated {gap_count} test report findings into the change manifest; source drift detection skipped.", then list each gap §1 did not route, as `{id}: {title} ({category})`, and say this run does not repair them. Then take the branch that applies:
|
|
142
|
+
|
|
143
|
+
- **`detect_only_mode == true`** → display "**Detect-only mode: skipping re-extract, merge and write.** Loading report..." and load, read the full file, then execute `{reportFile}` (report.md), which emits status `detect-only`.
|
|
144
|
+
- **No gap translated** (`gap_count` is 0, an empty manifest) → load, read the full file, then execute `{reportFile}` (report.md), which emits status `no-changes`.
|
|
145
|
+
- **Otherwise** → continue to §3.
|
|
146
|
+
|
|
147
|
+
### 3. Verify the Workspace Holds the Pinned Commit
|
|
148
|
+
|
|
149
|
+
The spot-checks read source at recorded `source_line` positions and must see the exact bytes the skill was pinned against. A drifted workspace silently verifies against the wrong tree: moved or renamed symbols appear "verified" because the recorded line now points at different code. Before reading any source, run the guard via `{checkWorkspaceDriftHelper}`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
uv run {checkWorkspaceDriftHelper} <source-root> \
|
|
153
|
+
--pinned-commit "<metadata.source_commit>" \
|
|
154
|
+
[--source-ref "<metadata.source_ref>"] \
|
|
155
|
+
[--allow-drift]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Pass `--allow-drift` only when the user provided `--allow-workspace-drift` to update-skill. The helper accepts `""` and `"local"` as the "no pinned commit" sentinels; it also auto-skips when `source_root` is not a git working tree (bare checkout, tarball extract, etc.).
|
|
159
|
+
|
|
160
|
+
The helper emits a result envelope:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"status": "ok" | "skipped" | "mismatch" | "overridden",
|
|
165
|
+
"skip_reason": "no-pinned-commit" | "not-a-git-tree" | null,
|
|
166
|
+
"head_sha": "...", "head_short_sha": "...", "match_kind": "full" | "short-prefix" | null,
|
|
167
|
+
"log_message": "workspace_drift_check: ...",
|
|
168
|
+
"halt_message": "<multi-line user-facing message>" | null
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Bind `{workspace_drift_status}` ← `status` and `{head_short_sha}` ← `head_short_sha` in workflow context, and `{pinned_short_sha}` ← the first 7 characters of `metadata.source_commit`. Only `overridden` (`--allow-workspace-drift` was passed and HEAD is not the pinned commit) changes what later steps do: nothing read at HEAD says anything about the pinned commit, so update-skill takes no provenance line, signature, parameter list, return type or node kind from it. The drift gate below halts on every gap whose repair needs a line, signature, parameter list or return type from the tree, and on every rescope, whose stats recount would count the public API there. `ok` (HEAD holds the pinned commit, with or without the flag) and `skipped` change nothing: lines move and node kinds are looked up as usual.
|
|
173
|
+
|
|
174
|
+
**Dispatch on `status`:**
|
|
175
|
+
|
|
176
|
+
- **`ok` or `skipped`** (helper exit 0): log `log_message` and continue to §4.
|
|
177
|
+
- **`overridden`** (helper exit 0): log `log_message`, add `workspace_drift_overridden: HEAD {head_short_sha} is not pinned {pinned_short_sha}` to `warnings[]`, and surface the override in the final report: report.md §2's Mode row is where the report shows it, in the one text that section gives. Then run the drift gate below, and continue to §4 only when it passes. The override does not automatically re-pin `metadata.source_commit`; re-pinning is explicit user work (a normal-mode update records the commit it reads as `source_commit`; or re-create the skill).
|
|
178
|
+
- **`mismatch`** (helper exit 2): HALT immediately with status `halted-for-workspace-drift`. Display the helper's `halt_message` verbatim: it already substitutes `{pinned_commit}`, `{source_ref or "unset"}`, `{source_root}`, `{head_sha}`, and the suggested `git checkout` command. Do not proceed to §4. Step 4 (merge) has not run; no partial writes. The halt procedure takes `phase: "re-extract:workspace-drift"`, `path: "{source_root}"`, `reason: "workspace HEAD {head_short_sha} is not the pinned commit {pinned_short_sha}"`.
|
|
179
|
+
- **Any other result** (the helper exits non-zero without a `mismatch` envelope, prints no JSON, or returns a status not listed above): HALT with status `blocked`, since the guard could not tell which commit the spot-checks would read. Show the helper's stderr. Do not proceed to §4. Step 4 (merge) has not run; no partial writes. The halt procedure takes `phase: "re-extract:workspace-drift"`, `path: "{source_root}"`, `reason: "drift-check-failed: {what the helper printed, or no JSON}"`.
|
|
180
|
+
|
|
181
|
+
**Drift gate (only when `{workspace_drift_status}` is `overridden`).** Run §4 bullet 1 now: with `--drift-status overridden` its plan checks this gate before any spot-check, and lists in `drift_blocked[]` every manifest entry whose repair needs something read from the tree: under the override that tree is HEAD, not the pinned commit, and update-skill writes nothing read there, neither a provenance line nor a signature, parameter list, return type or node kind. Every `NEW_EXPORT` and every `MODIFIED_EXPORT` needs it, whatever its `severity` and whether the provenance map holds the export: merge Priority 4 replaces a modified export's content with a fresh extraction, merge Priority 5 appends a new export's content, and write.md §3 adds an entry for an export the map does not hold, and in gap-driven mode each of those could only be read from the tree. That includes a gap §4 bullet 1 would send to §4a with nothing to scan (a blocking severity as bullet 1 defines it, no `source_citation` and an empty `resolved_paths[]`): under the override this gate halts on it first. Every `DELETED_EXPORT` needs the tree as well: in gap-driven mode it is a rescope (rule R1), which narrows the brief's scope, and write.md §2 must then recount `exports_public_api` and `exports_internal` over the narrowed scope from the tree, while under the override it keeps the counts metadata.json records. A rule R5 `MOVED_EXPORT`, a `STRUCTURAL_FIX` (a split-body consistency finding among them) and a `metadata update` need nothing from the tree and pass. Each item gives its entry's `reason`, the first that fits, read from the entry's `map_match` (§1):
|
|
182
|
+
|
|
183
|
+
- `a public API recount from the tree (rule R1)`: a rescope (`DELETED_EXPORT`);
|
|
184
|
+
- `a line from the tree (rule R3)`: a provenance-completeness gap (`provenance_completeness: true`), which §4 bullet 1 routes to §4a;
|
|
185
|
+
- `a line and a signature from the tree`: an export whose `map_match` is `not-found`;
|
|
186
|
+
- `a signature from the tree`: an export the map holds (`found` or `ambiguous`).
|
|
187
|
+
|
|
188
|
+
When its `status` is not `drift-blocked`, go on with its output in §4. Otherwise HALT with status `halted-for-workspace-drift` before merge runs, so merge writes nothing and §4a never runs. A rescope's amendment is not in the skill brief yet (rule R1): step 4 §6b writes it, so this halt leaves the brief as it was, and a re-run asks again. Display, with `{source_commit}` and `{source_ref}` read from metadata.json:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
Workspace drift blocks {N} gap(s) that need the pinned tree.
|
|
192
|
+
|
|
193
|
+
pinned (metadata.source_commit): {source_commit}
|
|
194
|
+
{if source_ref is set: pinned ref (metadata.source_ref): {source_ref}}
|
|
195
|
+
workspace HEAD ({source_root}): {head_short_sha}
|
|
196
|
+
|
|
197
|
+
--allow-workspace-drift reads HEAD, not the pinned commit, and update-skill
|
|
198
|
+
writes nothing read at HEAD: no provenance line, signature, parameter list,
|
|
199
|
+
return type or node kind.
|
|
200
|
+
|
|
201
|
+
Gaps that need the pinned tree:
|
|
202
|
+
{for each entry: - {name} ({change_category}, {severity or "no severity"}): {a public API recount from the tree (rule R1) | a line from the tree (rule R3) | a line and a signature from the tree | a signature from the tree}}
|
|
203
|
+
|
|
204
|
+
Fix one of the following, then re-run update-skill:
|
|
205
|
+
a) Check out the pinned commit: git -C "{source_root}" checkout {source_commit}
|
|
206
|
+
Then re-run --from-test-report without --allow-workspace-drift.
|
|
207
|
+
b) Run a normal update (without --from-test-report), which records the commit
|
|
208
|
+
it reads as the new pin, then re-run test-skill.
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The halt procedure takes `phase: "re-extract:workspace-drift"`, `path: "{source_root}"`, `reason: "drift-override: {N} gap(s) need the pinned tree: {name} ({reason}), ..."`.
|
|
212
|
+
|
|
213
|
+
### 4. Spot-Check Each Gap's Export
|
|
214
|
+
|
|
215
|
+
Spot-check each gap's citation instead of re-extracting the files: confirm each export the report names is still at its recorded location. Both calls below run `gap-records` (resolve `{buildChangeManifestHelper}` ← first existing path in `{buildChangeManifestProbeOrder}`) from `{project-root}`: it runs each spot-check with the definition-line rules write.md §6's verifier applies, routes each entry and writes the records, reading the gap-derived manifest §1 wrote from `{run_dir}/change-manifest.json` (each entry with its `map_match`), never from memory. The "Gap records" section of its docstring is the one statement of that routing and of each record's `verification` outcome: you answer what it asks and run the targeted re-extraction (§4a) it plans.
|
|
216
|
+
|
|
217
|
+
1. **Spot-check and route.** Run:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
uv run {buildChangeManifestHelper} gap-records --plan \
|
|
221
|
+
--manifest "{run_dir}/change-manifest.json" \
|
|
222
|
+
--provenance-map "{provenance_map_path}" \
|
|
223
|
+
[--source-root "{source_root}"] \
|
|
224
|
+
--drift-status "{workspace_drift_status}" \
|
|
225
|
+
[--judgments "{run_dir}/gap-judgments.json"] \
|
|
226
|
+
--files-out "{run_dir}/remediation-files.json"
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Pass `--source-root` unless the skill is docs-only, and `--judgments` once the file exists. Act on its `status`:
|
|
230
|
+
|
|
231
|
+
- **`drift-blocked`:** §3's drift gate halts.
|
|
232
|
+
- **`blocked`** (`rescope_without_amendment[]`): a `DELETED_EXPORT` whose manifest entry carries no `rescope` object (its `scope.amendments[]` entry, `action: "excluded"`, and its `scope.exclude` path, rule R1). HALT with status `blocked` (halt procedure: `phase: "re-extract:rescope"`, `reason: "rescope-without-amendment: {name}"`): a rescope without a brief scope amendment is denominator deflation and must not be written.
|
|
233
|
+
- **`needs-judgment`:** each item is a spot-check whose `file` the definition-line rules cover no language of. Read the file and add, under the item's `gap_id` in `{run_dir}/gap-judgments.json`, its `declaring_line`: the line that declares the export itself, never a decorator or comment above it, or null when no line does. Then run it again.
|
|
234
|
+
- **`planned`:** when `reextract.entries` is not empty, run §4a for them: the plan wrote their files to `{run_dir}/remediation-files.json`. Add none of its `warnings[]`: bullet 2's call prints them again.
|
|
235
|
+
- **Exit 1 or no JSON:** HALT with status `blocked` (halt procedure: `phase: "re-extract:gap-records"`, its stderr as `reason`).
|
|
236
|
+
|
|
237
|
+
A severity is blocking unless it is `Medium`, `Low` or `Info`, compared case-insensitively, so a missing or unrecognized one is blocking, like `Critical` and `High`. A blocking gap must produce provenance read from the source or halt in this file before merge: only a `Medium`, `Low` or `Info` gap may degrade to `unknown`.
|
|
238
|
+
2. **Write the gap-driven records** to `{run_dir}/reextract-records.json`, before §5 sends the run on: merge reads them, and step 5's `apply` writes the provenance map from them. Run the same call without `--plan` and `--files-out`:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
uv run {buildChangeManifestHelper} gap-records \
|
|
242
|
+
--manifest "{run_dir}/change-manifest.json" \
|
|
243
|
+
--provenance-map "{provenance_map_path}" \
|
|
244
|
+
[--source-root "{source_root}"] \
|
|
245
|
+
--drift-status "{workspace_drift_status}" \
|
|
246
|
+
[--judgments "{run_dir}/gap-judgments.json"] \
|
|
247
|
+
[--remediation-records "{run_dir}/remediation-records.json"] \
|
|
248
|
+
[--evidence "{run_dir}/evidence-records.jsonl" --tier "{forge_tier}"] \
|
|
249
|
+
-o "{run_dir}/reextract-records.json"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Pass `--remediation-records`, `--evidence` and `--tier` when §4a ran. It writes one verification record per export-bearing entry, the confidence breakdown and counts, and in `files` each record of `{run_dir}/remediation-records.json` it matched by name to an entry §4a scanned for, within that entry's own `resolved_paths[]`, copied as `records` wrote it: never type a record or count one by hand. With `--evidence` it appends its `targeted_reextraction` record to `{run_dir}/evidence-records.jsonl` as one JSON line keyed `targeted_reextraction`, which the evidence report (step 5 §4) shows beside the verified, moved and missing tally. Act on its `status`:
|
|
253
|
+
|
|
254
|
+
- **`unresolved`:** an entry routed to §4a has no record of its name in its path set, or had no path to scan (`files_scanned: 0`). A found export the public-reachability gate below finds internal is a resolution, not a failure. HALT with status `halted-for-remediation-path`, under `--dry-run` too (the run stops here instead of exiting `dry-run`), and display a consolidated report of every `unresolved[]` entry:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
Targeted re-extraction failed for {N} gap(s).
|
|
258
|
+
|
|
259
|
+
A blocking gap (any severity but Medium, Low or Info, a missing or
|
|
260
|
+
unrecognized one included) must resolve to AST provenance. The Remediation
|
|
261
|
+
text for the entries below does not name a file that contains the expected
|
|
262
|
+
export, so the workflow cannot produce a non-null `source_file` /
|
|
263
|
+
`source_line` without hallucinating.
|
|
264
|
+
|
|
265
|
+
Unresolved entries:
|
|
266
|
+
{for each entry in unresolved[]:}
|
|
267
|
+
- {name} ({severity or "no severity"})
|
|
268
|
+
remediation_paths: {paths, or "none named"}
|
|
269
|
+
rejected_paths: {each refused path (its reason), or "none"}
|
|
270
|
+
files_scanned: {count}
|
|
271
|
+
exports_matched: 0
|
|
272
|
+
|
|
273
|
+
Fix one of the following, then re-run update-skill:
|
|
274
|
+
a) Add a `file:line` citation to the Gap Report `Source:` field.
|
|
275
|
+
b) Edit the Remediation text to name the file(s) that actually contain the export(s).
|
|
276
|
+
c) Downgrade the gap(s) to Medium/Low/Info (accepts the degraded documentation outcome).
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Step 4 (merge) has not run; no partial writes. The halt procedure takes `phase: "re-extract:targeted-reextraction"`, `reason: "targeted re-extraction failed for {N} gap(s): {name} ({severity or no severity}), ..."`. **Why halt instead of degrading to `unknown`:** a null citation on a blocking gap lets the skill pass re-test while it still hides the broken behavior behind a placeholder.
|
|
280
|
+
- **`needs-judgment`:** for each item, add your answer to each of its `needs` under its `gap_id` in `{run_dir}/gap-judgments.json`, then run it again:
|
|
281
|
+
- **Public-reachability gate (`NEW_EXPORT` only):** `reachability`, for each `NEW_EXPORT` it located (`verified`, `moved` or `re-extracted`, at the item's `file` and `line`). Confirm the symbol is reachable as **public API**, not merely that a `pub` / `export` / top-level definition exists at the citation: resolve the symbol's module path, and confirm at least one of: (a) it is re-exported from the package entry-point barrel (`lib.rs` `pub use`, `index.ts` / `index.js` export, `__init__.py` import or `__all__`), or (b) every ancestor module on the path from that barrel is public (Rust `pub mod`, an exported TS namespace, a non-underscore Python package). A `pub(crate)` / `pub(super)` item, or one under a private module (e.g. `mod authority;` declared without `pub`), is **not** reachable. Answer `public`, or `internal-unreachable`: the helper then drops it from the export-bearing set, so it is never written to the documented `exports[]` array or SKILL.md, and lists it in `reclassified[]`. Do **not** hand-set any `stats` count: write.md §2's automatic recount derives `exports_internal` / `exports_total` from the merged surface and owns those fields. This honors the workflow rule that documented API must be importable by users, since a `pub`-but-internal symbol would otherwise inflate the documented public surface with a type users cannot import.
|
|
282
|
+
- `docstring`, for each `re-extracted` export: the docstring at its line, or null when it has none. Its record in `files` takes it, so step 4 Priority 5 merges the record as it merges a normal run's extraction.
|
|
283
|
+
- **`written`:** keep its `counts` for §5, and add its `warnings[]` to `warnings[]` (among them `provenance: spot-checks not run: skf-verify-provenance-completeness.py is missing; re-install SKF`, when every check recorded `unknown` for want of the verifier). Its `forwarded[]` entries (`STRUCTURAL_FIX`, rule R2, and `metadata update`, rule R4) go to merge as the manifest holds them, with no spot-check, no provenance lookup and no `entries[]` change; each export `reclassified[]` names goes to merge as a `metadata update` (rule R4) recording `reclassified: internal-unreachable` for the evidence report.
|
|
284
|
+
- **Exit 1 or no JSON:** HALT with status `blocked` (halt procedure: `phase: "re-extract:gap-records"`, its stderr as `reason`).
|
|
285
|
+
|
|
286
|
+
write.md §3 (step 5) accepts null `source_file` / `source_line` only for an `unknown` `Medium`, `Low` or `Info` gap the map does not hold, and leaves unchanged an entry whose definition lines the rules do not recognize, which may be a shape they do not cover: never call it gone.
|
|
287
|
+
3. Set `no_reextraction: true` in workflow context: step 5 will use this flag to skip stale `source_file`/`source_line`/`confidence` field updates for `verified` exports, except the label relabel write.md §2 applies when its stats helper flags an entry whose labels disagree with its `extraction_method`. `moved` exports get updated citations; a cited `NEW_EXPORT` whose spot-check pinned a line gets a new `source-read` (`T1-low`) entry at that line, since the verifier's text rules found that line, not an ast-grep recipe; `re-extracted` exports get full fresh provenance from §4a's extraction records (see step 5 §3). The flag is a global gap-driven marker, not a per-entry one: step 5 dispatches on each verification outcome independently.
|
|
288
|
+
4. Go on to §5. §4a below runs only where bullet 1 calls it.
|
|
289
|
+
|
|
290
|
+
### 4a. Targeted Re-Extraction Branch (Helper, Called from §4 Bullet 1)
|
|
291
|
+
|
|
292
|
+
**Do not execute this section sequentially.** It is a helper procedure §4 bullet 1 invokes for the entries its plan lists in `reextract.entries`. A run in which §4 bullet 1 routes no entry here skips this section entirely.
|
|
293
|
+
|
|
294
|
+
**Used by:** §4 bullet 1's plan, for the entries it lists in `reextract.entries`: each scans its `resolved_paths[]` (an entry with none has nothing to scan), and §4 bullet 2 matches what this section extracts to each entry by name.
|
|
295
|
+
|
|
296
|
+
**Purpose:** produce AST-backed provenance for a gap with a blocking `severity` (§4 bullet 1) and no citation that pins a line, so step 5 §3 never writes `source_file: null` for a blocking finding. Honors the workflow-level rule **Never hallucinate** (every statement must have AST provenance) against the most common gap-driven trigger: a failing test report whose Gap Report `Source:` field is a region reference (e.g., `@storybook/addon-docs control primitives`) rather than a `file:line` pair.
|
|
297
|
+
|
|
298
|
+
**Procedure:**
|
|
299
|
+
|
|
300
|
+
1. **Source access:** read the files under `{source_root}`, the tree §3 confirmed holds the pinned commit, never through the gh contents API, zread or deepwiki, which may serve another commit.
|
|
301
|
+
2. **The file set** is `{run_dir}/remediation-files.json`, which §4 bullet 1's plan wrote: the `resolved_paths[]` of every entry routed here, as they are. §1's `{parseGapsHelper}` resolved them under `{source_root}` and refused, into `rejected_paths[]`, every path that leaves it (`outside-root`, or `symlink-outside-root` through a link) or names no file (`not-found`, `no-match`). Remediation text is user-editable: never scan a refused path, and never expand or check a path by hand.
|
|
302
|
+
3. **Extract:** read the file set with the tier-appropriate extractor: `records` below labels each export by the tool that produced it, T1 (`extraction_method: ast-grep`) for an ast-grep match and T1-low (`extraction_method: source-read`) for an export read by eye. At Forge tier and above, run the recipe runner over the file set (resolve `{extractPublicApiHelper}` ← first existing path in `{extractPublicApiProbeOrder}`), from `{project-root}`:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
uv run {extractPublicApiHelper} --mode full \
|
|
306
|
+
--source-root "{source_root}" \
|
|
307
|
+
--files-from "{run_dir}/remediation-files.json" \
|
|
308
|
+
[--scope-type "{scope_type}"] \
|
|
309
|
+
--head-cap 0 \
|
|
310
|
+
-o "{run_dir}/remediation-exports.json"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Pass `--scope-type` when step 1 read a `scope_type` from metadata.json, so a component library runs its recipe set. It takes no `--brief`: a test report can name a file the brief's scope leaves out as an export's home, and the runner would skip it. On exit 1 (`incomplete`) run it once more; on exit 2 or 3, no JSON, or no candidate resolves, or at Quick tier, read the files by eye (by text pattern). Read by eye only what the recipes leave out: an export a file defines in a form Known Limitation #11 in `{extractionPatternsData}` lists, a file `file_issues[]` names and a name `entry_point_diff.extraction_gaps[]` lists. Write each export read by eye, and the `params` and `return_type` of each function export whose `params` the runner left null, in the shape detect-changes.md §2.1 Category B step 2 gives them, to `{run_dir}/remediation-details.json` as `{"exports": [...]}`. Launch subprocesses in parallel (Pattern 4) when available; sequential fallback otherwise. Follow the tier-degradation rules in `{tierDegradationRulesData}` when AST tools fail on individual files. Then, from `{project-root}`, build the file set's records:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
uv run {buildChangeManifestHelper} records \
|
|
317
|
+
[--extraction "{run_dir}/remediation-exports.json"] \
|
|
318
|
+
[--export-details "{run_dir}/remediation-details.json"] \
|
|
319
|
+
--files-from "{run_dir}/remediation-files.json" \
|
|
320
|
+
-o "{run_dir}/remediation-records.json"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Pass each input this item wrote. It writes one record per export, with its labels and `params` in the provenance map's typed form: never type a record. On exit 1, no JSON, or no candidate resolves: HALT with status `blocked` (halt procedure: `phase: "re-extract:records"`, its stderr as `reason`).
|
|
324
|
+
|
|
325
|
+
### 5. Display the Repair Summary and Route
|
|
326
|
+
|
|
327
|
+
Display the summary below, with the `counts` §4 bullet 2 printed:
|
|
328
|
+
|
|
329
|
+
"**Gap-driven re-extraction.** Verified {verified_count}/{gap_count} citations against live source. Moved: {moved_count}. Missing: {missing_count}. Re-extracted (via remediation paths or provenance-completeness, §4a): {re_extracted_count}. Rescoped (removed from surface): {rescoped_count}. Unknown (not in provenance map, a `Medium`, `Low` or `Info` gap §4a found nothing for, no single definition line, or a line the drift override kept from being moved): {unknown_count}."
|
|
330
|
+
|
|
331
|
+
When `{workspace_drift_status}` is `overridden`, add: "Every check read HEAD {head_short_sha}, not pinned {pinned_short_sha}: no line was moved or pinned."
|
|
332
|
+
|
|
333
|
+
This step asks nothing after §1's rule R1 prompts. Load and fully read the next file, then execute it, per the branch that applies:
|
|
334
|
+
|
|
335
|
+
- **`dry_run_mode == true`** → display "**Dry-run mode: skipping merge and write.** Loading report..." and load `{reportFile}` (report.md, NOT `{nextStepFile}`); it emits status `dry-run` describing what merge and write would have done, so a gap-driven `--dry-run` writes nothing. A halt in §3 or §4 (the drift gate, the targeted re-extraction) stops a `--dry-run` as it stops any other run.
|
|
336
|
+
- **Otherwise** → display "**Proceeding to merge...**" and load `{nextStepFile}` (merge.md) to begin the merge operation.
|
|
@@ -3,24 +3,50 @@
|
|
|
3
3
|
# (`{project-root}/_bmad/skf/` when installed, `{project-root}/src/` during
|
|
4
4
|
# development), NOT relative to this step file.
|
|
5
5
|
nextStepFile: 'shared/health-check.md'
|
|
6
|
+
# Resolve `{sourceTreeHelper}` to the first existing path. Step 1b removes
|
|
7
|
+
# the private source tree init.md §6b prepared. If neither path exists,
|
|
8
|
+
# skip the call and continue: a later run removes a tree left behind once
|
|
9
|
+
# it is seven days old.
|
|
10
|
+
sourceTreeProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-source-tree.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-source-tree.py'
|
|
13
|
+
# Resolve `{runLockHelper}` to the first existing path. If neither exists,
|
|
14
|
+
# say the lock stays and continue: a later update replaces it once stale.
|
|
15
|
+
runLockProbeOrder:
|
|
16
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-run-lock.py'
|
|
17
|
+
- '{project-root}/src/shared/scripts/skf-run-lock.py'
|
|
6
18
|
---
|
|
7
19
|
|
|
8
20
|
<!-- Config: communicate in {communication_language}. -->
|
|
9
21
|
|
|
10
|
-
# Step
|
|
22
|
+
# Step 7: Workflow Health Check
|
|
11
23
|
|
|
12
24
|
## STEP GOAL:
|
|
13
25
|
|
|
14
|
-
Chain to the shared workflow self-improvement health check at `{nextStepFile}`. This is the terminal step of update-skill
|
|
26
|
+
Chain to the shared workflow self-improvement health check at `{nextStepFile}`. This is the terminal step of update-skill: after the shared health check completes, the workflow is fully done. This step only releases the concurrency lock and the private source tree and removes this update's run folder, then delegates: no user-facing reports, file writes, or result contracts here (those belong in step 6), apart from one line when the lock or the tree stays on disk, or the folder does.
|
|
15
27
|
|
|
16
28
|
## Steps
|
|
17
29
|
|
|
18
|
-
1. **Release the concurrency lock**
|
|
30
|
+
1. **Release the concurrency lock** init.md §1b took (skip when `detect_only_mode` or `dry_run_mode` is true: those modes take none). Resolve `{runLockHelper}` ← first existing path in `{runLockProbeOrder}` and, from `{project-root}`, run:
|
|
19
31
|
|
|
20
32
|
```bash
|
|
21
|
-
|
|
33
|
+
uv run {runLockHelper} release \
|
|
34
|
+
--lock "{forge_data_folder}/{skill_name}/.skf-update.lock" \
|
|
35
|
+
--owner "{lock_owner}"
|
|
22
36
|
```
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
Never stop on the result:
|
|
39
|
+
|
|
40
|
+
- `released` true, or `reason` `absent`: continue.
|
|
41
|
+
- `reason` `not-owner`: another update took the lock over after this run's lock went stale, and the helper left that run's lock in place. Tell the user in one line "The run lock {forge_data_folder}/{skill_name}/.skf-update.lock now belongs to {held_by}: another update of {skill_name} took it over while this one ran.", then continue.
|
|
42
|
+
- No candidate resolves, or the command fails or prints no JSON: tell the user in one line "The run lock {forge_data_folder}/{skill_name}/.skf-update.lock was not released ({the first stderr line, or 'skf-run-lock.py is missing'}); delete it when no update of {skill_name} is running.", then continue.
|
|
43
|
+
|
|
44
|
+
1b. **Remove the private source tree** init.md §6b prepared, in every mode (`--detect-only` and `--dry-run` included), when `{source_tree}` is set: resolve `{sourceTreeHelper}` ← first existing path in `{sourceTreeProbeOrder}` and, from `{project-root}`, run `uv run {sourceTreeHelper} close --tree "{source_tree}"`. `{source_tree}` is the `tree` path the helper's `open` printed in init.md §6b; close also takes the run folder that holds it (where `changed-files.json` sits), and refuses any other folder. Bind `{source_tree_close}` ← `status` and `{source_tree_close_warnings}` ← `warnings`, and never stop on the result:
|
|
45
|
+
|
|
46
|
+
- `removed` or `missing`: continue.
|
|
47
|
+
- `refused`: run the command once more with `--tree` set to the exact `tree` value `open` printed, and bind its result the same way.
|
|
48
|
+
- `left`, `refused` again, or the command fails or prints no JSON: tell the user in one line "The private source tree {source_tree} was not removed ({source_tree_close}, {source_tree_close_warnings}); a later update removes it once it is seven days old.", then continue.
|
|
49
|
+
|
|
50
|
+
1c. **Remove this update's run folder** `{run_dir}` in every mode (`--detect-only` and `--dry-run` included), when `{run_dir}` is bound: from `{project-root}`, run `rm -rf "{run_dir}"`. It holds this run's state, its decision log and warnings (step 6 listed the warnings in a read-only run's report, a headless run's line carried both, and a finished run's result files hold both), and the helper files steps 2 to 5 passed between them; write.md §10 already dropped the snapshot a gap-driven repair took. When it fails, tell the user in one line "The run folder {run_dir} was not removed; delete it.", then continue.
|
|
25
51
|
|
|
26
52
|
2. Load `{nextStepFile}`, read it fully, then execute it.
|