bmad-module-skill-forge 1.5.1 → 1.7.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/docs/_data/pinned.yaml +1 -1
- package/docs/workflows.md +1 -1
- package/package.json +2 -2
- package/src/knowledge/version-paths.md +4 -3
- package/src/shared/health-check.md +1 -1
- package/src/shared/scripts/schemas/skill-brief.v1.json +5 -0
- package/src/shared/scripts/skf-atomic-write.py +4 -1
- package/src/shared/scripts/skf-description-guard.py +33 -80
- package/src/shared/scripts/skf-detect-language.py +34 -0
- package/src/shared/scripts/skf-detect-workspaces.py +54 -0
- package/src/shared/scripts/skf-enumerate-stack-skills.py +107 -16
- package/src/shared/scripts/skf-forge-tier-rw.py +4 -1
- package/src/shared/scripts/skf-manifest-ops.py +8 -4
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +4 -1
- package/src/shared/scripts/skf-qmd-classify-collections.py +29 -3
- package/src/shared/scripts/skf-rebuild-managed-sections.py +72 -7
- package/src/shared/scripts/skf-scan-skill-md-structure.py +13 -3
- package/src/shared/scripts/skf-validate-brief-inputs.py +79 -18
- package/src/shared/scripts/skf-validate-brief-schema.py +47 -1
- package/src/shared/scripts/skf-validate-frontmatter.py +60 -2
- package/src/shared/scripts/skf-write-skill-brief.py +97 -17
- package/src/skf-analyze-source/assets/skill-brief-schema.md +10 -2
- package/src/skf-analyze-source/references/continue.md +3 -1
- package/src/skf-analyze-source/references/generate-briefs.md +30 -9
- package/src/skf-analyze-source/references/recommend.md +1 -1
- package/src/skf-brief-skill/SKILL.md +4 -2
- package/src/skf-brief-skill/assets/skill-brief-schema.md +6 -1
- package/src/skf-brief-skill/references/analyze-target.md +22 -6
- package/src/skf-brief-skill/references/confirm-brief.md +4 -0
- package/src/skf-brief-skill/references/gather-intent.md +72 -3
- package/src/skf-brief-skill/references/headless-args.md +3 -2
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +16 -8
- package/src/skf-brief-skill/references/scope-definition.md +20 -2
- package/src/skf-brief-skill/references/version-resolution.md +1 -1
- package/src/skf-brief-skill/references/write-brief.md +12 -2
- package/src/skf-create-skill/assets/compile-assembly-rules.md +20 -1
- package/src/skf-create-skill/assets/skill-sections.md +2 -1
- package/src/skf-create-skill/assets/tessl-dismissal-rules.md +18 -5
- package/src/skf-create-skill/references/compile.md +5 -2
- package/src/skf-create-skill/references/extract.md +8 -3
- package/src/skf-create-skill/references/extraction-patterns.md +14 -5
- package/src/skf-create-skill/references/generate-artifacts.md +5 -1
- package/src/skf-create-skill/references/source-resolution-protocols.md +5 -2
- package/src/skf-create-skill/references/validate.md +4 -3
- package/src/skf-create-stack-skill/assets/provenance-map-schema.md +3 -3
- package/src/skf-create-stack-skill/assets/stack-skill-template.md +56 -0
- package/src/skf-create-stack-skill/references/compile-stack.md +13 -2
- package/src/skf-create-stack-skill/references/compose-mode-rules.md +2 -0
- package/src/skf-create-stack-skill/references/detect-integrations.md +5 -2
- package/src/skf-create-stack-skill/references/generate-output.md +49 -9
- package/src/skf-create-stack-skill/references/validate.md +8 -5
- package/src/skf-export-skill/references/load-skill.md +5 -4
- package/src/skf-export-skill/references/manifest-rebuild.md +1 -1
- package/src/skf-export-skill/references/token-report.md +2 -2
- package/src/skf-export-skill/references/update-context.md +25 -11
- package/src/skf-quick-skill/assets/skill-template.md +2 -2
- package/src/skf-quick-skill/references/compile.md +4 -2
- package/src/skf-quick-skill/references/write-and-validate.md +2 -2
- package/src/skf-refine-architecture/SKILL.md +1 -1
- package/src/skf-refine-architecture/references/gap-analysis.md +30 -4
- package/src/skf-refine-architecture/references/init.md +2 -0
- package/src/skf-refine-architecture/references/issue-detection.md +8 -3
- package/src/skf-test-skill/SKILL.md +1 -1
- package/src/skf-test-skill/references/coherence-check.md +4 -4
- package/src/skf-test-skill/references/coverage-check.md +58 -16
- package/src/skf-test-skill/references/external-validators.md +7 -7
- package/src/skf-test-skill/references/init.md +5 -4
- package/src/skf-test-skill/references/score.md +6 -3
- package/src/skf-test-skill/references/scoring-rules.md +12 -0
- package/src/skf-test-skill/references/source-access-protocol.md +19 -3
- package/src/skf-test-skill/scripts/compute-score.py +4 -1
- package/src/skf-update-skill/references/detect-changes.md +15 -2
- package/src/skf-update-skill/references/init.md +8 -1
- package/src/skf-update-skill/references/merge.md +22 -0
- package/src/skf-update-skill/references/re-extract.md +19 -7
- package/src/skf-update-skill/references/write.md +3 -0
- package/src/skf-verify-stack/SKILL.md +2 -2
- package/src/skf-verify-stack/assets/feasibility-report-template.md +1 -1
- package/src/skf-verify-stack/references/coverage.md +35 -6
- package/src/skf-verify-stack/references/init.md +6 -2
- package/src/skf-verify-stack/references/integration-verification-rules.md +1 -1
- package/src/skf-verify-stack/references/integrations.md +7 -3
- package/src/skf-verify-stack/references/report.md +5 -1
- package/src/skf-verify-stack/references/requirements.md +3 -1
- package/src/skf-verify-stack/references/synthesize.md +11 -3
- package/tools/cli/lib/installer.js +1 -0
|
@@ -6,9 +6,11 @@ Every shell snippet in this document uses `{...}` placeholders for paths. **Alwa
|
|
|
6
6
|
|
|
7
7
|
## Tag Resolution
|
|
8
8
|
|
|
9
|
-
Tag resolution maps a declared version in the brief onto a concrete git ref before cloning, so the skill is built from code matching its declared version.
|
|
9
|
+
Tag resolution maps a declared version in the brief onto a concrete git ref before cloning, so the skill is built from code matching its declared version. Three signals can drive it, in priority order: an explicit `brief.target_ref` (a ref the user states verbatim — highest priority), an **explicit** `brief.target_version` (deliberate user intent), or an **implicit** `brief.version` (auto-populated hint from `brief-skill`). All apply only when `source_repo` is a remote URL.
|
|
10
10
|
|
|
11
|
-
**
|
|
11
|
+
**Explicit ref override (when `target_ref` is set):** When `brief.target_ref` is present AND `source_repo` is a remote URL, use its value verbatim as `source_ref` and skip all version-to-tag matching below. This is the escape hatch for ref conventions the matching heuristics don't cover — notably monorepo crate tags whose prefix differs from the skill name (e.g. skill `livekit-rust` built from tag `livekit/v0.7.42`). Confirm it resolves first: `git ls-remote "{source_repo}" "{target_ref}"` (matches a tag or branch) — if it returns nothing, ⚠️ warn "`target_ref` ({target_ref}) does not resolve in {source_repo}; falling back to version matching" and continue with the version-based matching below.
|
|
12
|
+
|
|
13
|
+
**When none of `brief.target_ref`, `brief.target_version`, or `brief.version` is set:** skip tag resolution entirely. Set `source_ref` to `HEAD` (default branch).
|
|
12
14
|
|
|
13
15
|
### Explicit Tag Resolution (when target_version is set)
|
|
14
16
|
|
|
@@ -22,6 +24,7 @@ When `brief.target_version` is present AND `source_repo` is a remote URL, resolv
|
|
|
22
24
|
- **Exact match:** `{target_version}` (e.g., `0.5.0`)
|
|
23
25
|
- **With `v` prefix:** `v{target_version}` (e.g., `v0.5.0`)
|
|
24
26
|
- **With package scope (monorepos):** `{brief.name}@{target_version}` or `@{scope}/{brief.name}@{target_version}`
|
|
27
|
+
- **With crate/package-directory prefix (monorepos):** `{brief.name}/v{target_version}`, `{brief.name}/{target_version}`, or `{brief.name}-v{target_version}` (e.g. `tokio/v1.0.0`). Covers monorepos whose tags are prefixed by the crate/package directory **when that directory equals the skill name**. When the directory differs from the skill name (e.g. crate `livekit` for skill `livekit-rust`, tag `livekit/v0.7.42`), this heuristic can't infer it — set `target_ref` explicitly instead.
|
|
25
28
|
|
|
26
29
|
3. **Resolution outcomes:**
|
|
27
30
|
- **Single match:** Store the matched tag as `source_ref`. Use it as `{branch}` in all subsequent clone/API commands.
|
|
@@ -76,7 +76,7 @@ npx skill-check check <staging-skill-dir> --fix --format json {security_scan_fla
|
|
|
76
76
|
|
|
77
77
|
This performs frontmatter validation, description quality checks, body limit enforcement, local link resolution, file formatting, auto-fix of deterministic issues, and quality scoring (0-100) across five weighted categories.
|
|
78
78
|
|
|
79
|
-
**Parse the JSON output** for: `
|
|
79
|
+
**Parse the JSON output** for: `scores[].score` (0-100 — match the entry by `relativePath`/`skillId`; falls back to a top-level `qualityScore` on older skill-check builds), `diagnostics[]` (remaining issues), `fixed[]` (auto-corrected issues).
|
|
80
80
|
|
|
81
81
|
**Description Guard Protocol:** This invocation may modify SKILL.md (especially when `fixed[]` is non-empty). Wrap the `skill-check check --fix` call in the four-phase guard defined in §0 by invoking `{descriptionGuardHelper}` at the capture and verify-restore points:
|
|
82
82
|
|
|
@@ -94,7 +94,7 @@ uv run {descriptionGuardHelper} verify-restore <staging-skill-dir>/SKILL.md \
|
|
|
94
94
|
|
|
95
95
|
If `restored: true` in the verify-restore output, apply §0's post-restore re-validation hook. If `fixed[]` was non-empty in the skill-check output, also re-read the modified SKILL.md to sync the in-context copy before proceeding — this prevents silent divergence between the in-context and on-disk versions that step 7 will use for artifact generation.
|
|
96
96
|
|
|
97
|
-
**Note:** `skill-check` may return non-zero exit code even when `errorCount` is 0. Always rely on parsed JSON, not the shell exit code.
|
|
97
|
+
**Note:** `skill-check` may return non-zero exit code even when `summary.errorCount` is 0. Always rely on parsed JSON, not the shell exit code.
|
|
98
98
|
|
|
99
99
|
- **Score ≥ 70:** Record "Schema: PASS (score: {score}/100)" in evidence-report
|
|
100
100
|
- **Score < 70:** Log remaining diagnostics as warnings, record "Schema: WARN — score {score}/100, {count} remaining issues", proceed
|
|
@@ -182,7 +182,8 @@ Parse output for: `description_score`, `content_score`, `review_score`, `validat
|
|
|
182
182
|
**Apply dismissal rules** in this order:
|
|
183
183
|
|
|
184
184
|
1. **Check score thresholds** against the "Score Thresholds" table in `{tesslDismissalData}`. Most importantly:
|
|
185
|
-
- If `
|
|
185
|
+
- If tessl's output contains a `findings[]` entry with rule ID `description_field` (the deterministic angle-bracket / XML-tag validator): follow the **recover-then-halt** path defined by the `description-xml-tags-guarded-upstream` rule in `{tesslDismissalData}`. Re-apply step 5 §2a's `<`/`>` → `{`/`}` substitution in place on the staging SKILL.md frontmatter `description`, re-sync the in-context copy, and re-run `npx -y tessl skill review <staging-skill-dir>` once. **Re-run gate:** treat the `description_field` finding being **absent** from the re-run as the only successful recovery outcome. If the finding persists on the re-run — whether the re-substitution improved the description or not — that counts as recovery failure: halt with the original `description-xml-tags-guarded-upstream` failure message from `{tesslDismissalData}`, do NOT proceed to §6b, and do NOT downgrade the recovery to a warning. On successful recovery (finding cleared), log `description-recovery: applied ({count} substitutions)` in the evidence report under "Dismissed tessl suggestions" and continue suggestion iteration against the rerun's `judge_suggestions[]`.
|
|
186
|
+
- If the LLM-judge `description_score` is below 100 **but no `description_field` finding is present** (the deterministic validator PASSED): this is a soft discoverability signal (jargon density, trigger-term phrasing) from the judge's sub-scores, not a sanitizer bypass. Record `description_judge_score: {n}% (deterministic description_field validator PASSED)` as a warning in the evidence report and continue — do NOT trigger the recover-then-halt path and do NOT halt.
|
|
186
187
|
- If `review_score < 60` or `content_score < 60`: record warnings in the evidence report, continue.
|
|
187
188
|
2. **Iterate `judge_suggestions[]`.** For each suggestion:
|
|
188
189
|
- Cross-reference against the rules in `{tesslDismissalData}` in order.
|
|
@@ -53,7 +53,7 @@ Used when the workflow ran in code-mode against an actual codebase. `source_repo
|
|
|
53
53
|
|
|
54
54
|
## Compose-mode variant
|
|
55
55
|
|
|
56
|
-
Used when the workflow ran in compose-mode against pre-generated constituent skills. Source-anchor fields (`source_repo`, `source_commit`, `source_ref`) are `null` because there is no codebase to anchor against — provenance traces back to the constituent skills instead, captured in the `constituents[]` array. Each entry's `extraction_method` is `"compose-from-skill"`; integrations have `detection_method` of `"architecture_co_mention"` (named in the architecture doc) or `"inferred_from_shared_domain"` (synthesized inference,
|
|
56
|
+
Used when the workflow ran in compose-mode against pre-generated constituent skills. Source-anchor fields (`source_repo`, `source_commit`, `source_ref`) are `null` because there is no codebase to anchor against — provenance traces back to the constituent skills instead, captured in the `constituents[]` array. Each entry's `extraction_method` is `"compose-from-skill"`; integrations have `detection_method` of `"architecture_co_mention"` (named in the architecture doc), `"constituent_documented_contract"` (a cross-library contract documented in a constituent skill's integration docs but not co-mentioned in the architecture document — e.g. a grep-verified upstream seam cited from a source skill), or `"inferred_from_shared_domain"` (synthesized inference from shared language/domain, no cited contract). `detection_method` records *how* an edge was discovered; it is orthogonal to `confidence`, which is inherited from the constituent skills per the Confidence Tier Inheritance matrix in `references/compose-mode-rules.md` (the integration tier is the weaker of the pair — never forced to a fixed band by detection method).
|
|
57
57
|
|
|
58
58
|
```json
|
|
59
59
|
{
|
|
@@ -82,9 +82,9 @@ Used when the workflow ran in compose-mode against pre-generated constituent ski
|
|
|
82
82
|
{
|
|
83
83
|
"libraries": ["{libA}", "{libB}"],
|
|
84
84
|
"pattern_type": "{type}",
|
|
85
|
-
"detection_method": "architecture_co_mention|inferred_from_shared_domain",
|
|
85
|
+
"detection_method": "architecture_co_mention|constituent_documented_contract|inferred_from_shared_domain",
|
|
86
86
|
"co_import_files": [],
|
|
87
|
-
"confidence": "T2|T3"
|
|
87
|
+
"confidence": "T1|T1-low|T2|T3"
|
|
88
88
|
}
|
|
89
89
|
],
|
|
90
90
|
"constituents": [
|
|
@@ -49,6 +49,36 @@ description: >
|
|
|
49
49
|
- {convention_2}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## Sizing Guidance for Large Stacks
|
|
53
|
+
|
|
54
|
+
A stack capstone grows monotonically as patterns and libraries are added, so a
|
|
55
|
+
mature stack eventually pushes SKILL.md past skill-check's `body.max_lines`
|
|
56
|
+
budget (default **500**) and/or the `description` past 1024 chars. Pre-empt both
|
|
57
|
+
ceilings at compile time:
|
|
58
|
+
|
|
59
|
+
- **Catalog placement.** The `Library Reference Index` table and `Per-Library
|
|
60
|
+
Summaries` are the largest, most reference-like sections. For a **large stack**
|
|
61
|
+
(heuristic: **> 6 libraries OR > 6 integration patterns**, the point at which
|
|
62
|
+
the inline catalog crowds the 500-line budget), author both into
|
|
63
|
+
`references/stack-catalog.md` and leave only an inline pointer in SKILL.md (see
|
|
64
|
+
below). For a **small stack**, keep them inline — inline passive context yields
|
|
65
|
+
higher task accuracy than on-demand retrieval, and small stacks fit comfortably.
|
|
66
|
+
Integration Patterns and Conventions stay inline regardless (Tier 1, load-bearing).
|
|
67
|
+
- **Inline pointer form** (replaces the two sections above when extracted):
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
## Library Catalog
|
|
71
|
+
|
|
72
|
+
{lib_count} libraries indexed in [references/stack-catalog.md](references/stack-catalog.md) —
|
|
73
|
+
reference-index table + per-library summaries. Load it for a specific library's exports or role.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- **Description cap.** Keep `description` ≤ 1024 chars. Do NOT enumerate every
|
|
77
|
+
library by name; the generic "{lib_count} libraries with {integration_count}
|
|
78
|
+
integration patterns" form already scales. If a per-library parenthetical is
|
|
79
|
+
used, cap it to the top libraries by import/export count with a `+{N} more`
|
|
80
|
+
suffix — the full list lives in `metadata.json` `libraries[]` and the catalog.
|
|
81
|
+
|
|
52
82
|
## context-snippet.md Format (Vercel-Aligned)
|
|
53
83
|
|
|
54
84
|
Indexed format targeting ~80-120 tokens per stack:
|
|
@@ -107,6 +137,32 @@ Indexed format targeting ~80-120 tokens per stack:
|
|
|
107
137
|
}
|
|
108
138
|
```
|
|
109
139
|
|
|
140
|
+
## references/stack-catalog.md Structure
|
|
141
|
+
|
|
142
|
+
Written **only for large stacks** (see Sizing Guidance) when the catalog is
|
|
143
|
+
extracted out of SKILL.md. Holds the two sections verbatim from the inline form:
|
|
144
|
+
|
|
145
|
+
```markdown
|
|
146
|
+
# {project_name} Stack — Library Catalog
|
|
147
|
+
|
|
148
|
+
> Reference index + per-library summaries extracted from SKILL.md to keep the
|
|
149
|
+
> capstone under the body-size budget. See SKILL.md for integration patterns.
|
|
150
|
+
|
|
151
|
+
## Library Reference Index
|
|
152
|
+
|
|
153
|
+
| Library | Imports | Key Exports | Confidence | Reference |
|
|
154
|
+
|---------|---------|-------------|------------|-----------|
|
|
155
|
+
| {name} | {count} | {top_exports} | {tier} | [ref](references/{name}.md) |
|
|
156
|
+
|
|
157
|
+
## Per-Library Summaries
|
|
158
|
+
|
|
159
|
+
### {library_name}
|
|
160
|
+
**Role in stack:** {one-line description of what this library does in this project}
|
|
161
|
+
**Key exports used:** {comma-separated list}
|
|
162
|
+
**Usage pattern:** {brief pattern description}
|
|
163
|
+
**Confidence:** {T1/T1-low/T2}
|
|
164
|
+
```
|
|
165
|
+
|
|
110
166
|
## references/{library}.md Structure
|
|
111
167
|
|
|
112
168
|
```markdown
|
|
@@ -41,7 +41,7 @@ description: >
|
|
|
41
41
|
**Frontmatter rules:**
|
|
42
42
|
|
|
43
43
|
- `name`: lowercase alphanumeric + hyphens only, must match skill output directory name. **Stack skills MUST end in `-stack`** (e.g., `{project_name}-stack`) — this is how consumers (skf-verify-stack, skf-test-skill) detect stack vs individual skills.
|
|
44
|
-
- `description`: non-empty, max 1024 chars, trigger-optimized for agent discovery. MUST use third-person voice ("Processes..." not "I can..." or "You can...").
|
|
44
|
+
- `description`: non-empty, max 1024 chars, trigger-optimized for agent discovery. MUST use third-person voice ("Processes..." not "I can..." or "You can..."). **Do NOT enumerate every library by name** — a 12+ library stack overruns 1024 chars. Keep the generic "{lib_count} libraries with {integration_count} integration patterns" form; if a per-library parenthetical is used, cap it to the top libraries by import/export count with a `+{N} more` suffix (full list lives in `metadata.json` `libraries[]`). See "Sizing Guidance for Large Stacks" in `{stackSkillTemplate}`.
|
|
45
45
|
- No other frontmatter fields — only `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools` are permitted by spec
|
|
46
46
|
|
|
47
47
|
### 3. Compile Integration Layer
|
|
@@ -68,6 +68,16 @@ description: >
|
|
|
68
68
|
|
|
69
69
|
### 4. Compile Per-Library Sections
|
|
70
70
|
|
|
71
|
+
**Catalog placement (decide once, applies to this section and §6).** The
|
|
72
|
+
`Per-Library Summaries` and `Library Reference Index` are the largest sections
|
|
73
|
+
and grow with the stack. For a **large stack** (heuristic: **> 6 libraries OR
|
|
74
|
+
> 6 integration patterns**), author both into `references/stack-catalog.md`
|
|
75
|
+
(structure in `{stackSkillTemplate}`) and place only the inline pointer from the
|
|
76
|
+
template's "Sizing Guidance" in SKILL.md — this keeps the body under the 500-line
|
|
77
|
+
`body.max_lines` budget that step 08 enforces. For a **small stack**, keep both
|
|
78
|
+
inline (inline passive context yields higher task accuracy). Either way, step 08's
|
|
79
|
+
body-size gate is the backstop; step 08 (`validate.md` §4) accepts both forms.
|
|
80
|
+
|
|
71
81
|
For each confirmed library (ordered by integration connectivity, then import count — **in compose-mode**, order by integration connectivity, then skill confidence tier since import counts are not available):
|
|
72
82
|
|
|
73
83
|
- Role in stack (one-line description)
|
|
@@ -86,7 +96,8 @@ Extract project-specific conventions from the extractions:
|
|
|
86
96
|
|
|
87
97
|
### 6. Compile Library Reference Index
|
|
88
98
|
|
|
89
|
-
|
|
99
|
+
Place per the §4 catalog-placement decision (inline for small stacks; in
|
|
100
|
+
`references/stack-catalog.md` for large stacks). Create the reference index table:
|
|
90
101
|
|
|
91
102
|
| Library | Imports | Key Exports | Confidence | Reference |
|
|
92
103
|
|---------|---------|-------------|------------|-----------|
|
|
@@ -85,3 +85,5 @@ When no architecture document is available:
|
|
|
85
85
|
- Infer from skills sharing domain keywords in their `SKILL.md` descriptions
|
|
86
86
|
- Mark all inferred integrations: `[inferred from shared domain]`
|
|
87
87
|
- Inferred integrations default to lowest confidence of the pair with `[inferred from shared domain]` suffix (use this instead of `[composed]` for inferred integrations)
|
|
88
|
+
|
|
89
|
+
**Constituent-documented contracts (distinct from shared-domain inference):** When a constituent skill's own integration docs cite a verifiable cross-library contract (e.g. a grep-verified upstream seam) that the architecture document does not co-mention, record it with `detection_method: constituent_documented_contract` (see `assets/provenance-map-schema.md`) — NOT `inferred_from_shared_domain`. It is a cited contract, not a synthesized guess. Its confidence still inherits the weaker tier of the pair per the matrix above — detection method is orthogonal to tier and never forces a fixed band.
|
|
@@ -142,10 +142,13 @@ For each detected integration:
|
|
|
142
142
|
- **Assign confidence (M1/M3) — derive from per-library tiers + detection-method qualifier (NOT from AST):** integration detection here is grep + co-import (optionally CCC-augmented), never AST. The integration's confidence is the **weaker** of the two libraries' tiers from `per_library_extractions[]` (tie-break: T1-low > T1, T2 > T1-low, T3 > T2 — never overstate). Then append a detection-method qualifier:
|
|
143
143
|
- `grep-co-import` — the pair qualified via direct co-import grep (the default).
|
|
144
144
|
- `ccc-augmented` — the pair qualified only after CCC semantic search elevated it (per §2 CCC augmentation), and the post-hoc import verification (H3) confirmed both imports.
|
|
145
|
-
- `architecture-co-mention` — compose-mode pair qualified via word-boundary co-mention in the architecture document (per §2 H2 guards).
|
|
146
|
-
- `
|
|
145
|
+
- `architecture-co-mention` — compose-mode pair qualified via word-boundary co-mention in the architecture document (per §2 H2 guards). Maps to `detection_method: architecture_co_mention` in `provenance-map.json`.
|
|
146
|
+
- `constituent-documented-contract` — compose-mode pair whose cross-library contract is documented in a constituent skill's integration docs (cited, e.g. a grep-verified upstream seam) but not co-mentioned in the architecture document. Maps to `detection_method: constituent_documented_contract` in `provenance-map.json`.
|
|
147
|
+
- `inferred-shared-domain` — compose-mode pair without an architecture document, inferred from shared `language` or domain keywords (no cited contract). Maps to `detection_method: inferred_from_shared_domain` in `provenance-map.json`.
|
|
147
148
|
- Render as `{tier} ({qualifier})` — e.g., `T1-low (grep-co-import)`, `T1 (ccc-augmented)`, `T1-low (architecture-co-mention) [composed]`. The `[composed]`/`[inferred from shared domain]` suffix from `compose-mode-rules.md` is appended after the qualifier in compose-mode.
|
|
148
149
|
|
|
150
|
+
**Provenance ↔ SKILL.md tier parity:** The tier derived above is the single value for this edge — write the *same* tier to both the SKILL.md integration label and `provenance-map.json` `integrations[].confidence`. The detection-method qualifier (and the `provenance-map.json` `detection_method` it maps to) records *how* the edge was found and is orthogonal to confidence; it never forces the tier into a fixed band.
|
|
151
|
+
|
|
149
152
|
### 4. Build Integration Graph
|
|
150
153
|
|
|
151
154
|
Assemble the integration graph:
|
|
@@ -9,6 +9,14 @@ provenanceMapSchemaPath: 'assets/provenance-map-schema.md'
|
|
|
9
9
|
atomicWriteProbeOrder:
|
|
10
10
|
- '{project-root}/_bmad/skf/shared/scripts/skf-atomic-write.py'
|
|
11
11
|
- '{project-root}/src/shared/scripts/skf-atomic-write.py'
|
|
12
|
+
# Resolve `{frontmatterValidator}` by probing `{frontmatterValidatorProbeOrder}`
|
|
13
|
+
# in order (installed SKF module path first, src/ dev-checkout fallback); first
|
|
14
|
+
# existing path wins. Used by the §8 pre-commit frontmatter + body-size gate
|
|
15
|
+
# (`--max-body-lines`). If neither resolves, the gate degrades to a WARNING —
|
|
16
|
+
# step 8 (validate.md) still runs the full post-commit check.
|
|
17
|
+
frontmatterValidatorProbeOrder:
|
|
18
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-validate-frontmatter.py'
|
|
19
|
+
- '{project-root}/src/shared/scripts/skf-validate-frontmatter.py'
|
|
12
20
|
---
|
|
13
21
|
|
|
14
22
|
<!-- Config: communicate in {communication_language}. Artifact text in {document_output_language}. -->
|
|
@@ -29,7 +37,7 @@ Write all deliverable and workspace artifact files to their target directories.
|
|
|
29
37
|
|
|
30
38
|
### 1. Resolve Paths and Stage Target Directory
|
|
31
39
|
|
|
32
|
-
Resolve `{version}`
|
|
40
|
+
Resolve `{version}` per S11 below — the primary library version in code-mode, or the stack-local scheme in compose-mode (`1.0.0` for a new stack, or a bump of `{prior_stack_version}` on re-composition). The final artifact paths are:
|
|
33
41
|
|
|
34
42
|
```
|
|
35
43
|
{skill_group} # {skills_output_folder}/{project_name}-stack/
|
|
@@ -41,7 +49,14 @@ Resolve `{version}` from the primary library version or default to `1.0.0` (see
|
|
|
41
49
|
|
|
42
50
|
Where the skill name is `{project_name}-stack` and `{version}` is the semver version (with build metadata stripped per `knowledge/version-paths.md`).
|
|
43
51
|
|
|
44
|
-
**Primary library definition (S11):** In code-mode, the primary library is the dependency with the highest import count from step 3; its `version` (from the manifest) becomes `{primary_library_version}
|
|
52
|
+
**Primary library definition (S11):** In code-mode, the primary library is the dependency with the highest import count from step 3; its `version` (from the manifest) becomes `{primary_library_version}`, falling back to `1.0.0` if unavailable. In compose-mode, the stack carries its own release identity: default `{version}` to `1.0.0` (a stack-local scheme) — do NOT borrow the highest constituent semver. Constituent versions are preserved in `dependencies[]`, so no information is lost, and the package no longer reads as tracking whichever constituent happened to have the highest version. The `1.0.0` default applies only to a **genuinely new** stack — see the re-composition rule below.
|
|
53
|
+
|
|
54
|
+
**Re-composition versioning (S11, compose-mode):** When the S3 pre-flight resolves a `{prior_stack_version}` (re-composing a stack that already has a release line), continue that line instead of resetting — defaulting to `1.0.0` would publish a version *below* the existing release (e.g. `1.0.0` shadowing a prior `3.0.5`, a backward jump). Bump `{prior_stack_version}`:
|
|
55
|
+
|
|
56
|
+
- **Major** if any library in `{prior_libraries}` is removed or replaced — dropping a documented library is breaking for consumers of the stack.
|
|
57
|
+
- **Minor** otherwise — libraries only added, and/or integration content changed (backward-compatible).
|
|
58
|
+
|
|
59
|
+
Never emit a `{version}` ≤ `{prior_stack_version}`. Narrate the resolved version and the bump rationale.
|
|
45
60
|
|
|
46
61
|
**Pre-flight: group-dir type check (S3):** If `{skills_output_folder}/{project_name}-stack/` already exists, probe `{skills_output_folder}/{project_name}-stack/active/{project_name}-stack/metadata.json`. If that metadata exists and `skill_type != "stack"`, HALT with:
|
|
47
62
|
|
|
@@ -49,6 +64,8 @@ Where the skill name is `{project_name}-stack` and `{version}` is the semver ver
|
|
|
49
64
|
|
|
50
65
|
Do NOT proceed to staging or commit.
|
|
51
66
|
|
|
67
|
+
If that metadata exists and `skill_type == "stack"`, this run is a **re-composition**: capture its `version` as `{prior_stack_version}` and its `libraries` array as `{prior_libraries}` for the S11 re-composition rule above. If the group dir is absent — or exists but has no resolvable `active` stack metadata — treat this as a new stack and leave `{prior_stack_version}` unset.
|
|
68
|
+
|
|
52
69
|
**Atomic write strategy (C2 / B5):** All artifact writes for `{skill_package}` MUST stage into a temp directory first, then commit atomically via `skf-atomic-write.py commit-dir`. The active symlink flip only happens AFTER the commit succeeds.
|
|
53
70
|
|
|
54
71
|
Create the staging directory:
|
|
@@ -92,6 +109,8 @@ Load structure from `{stackSkillTemplate}` references section:
|
|
|
92
109
|
- Usage patterns with file:line citations (**in compose-mode**: usage patterns from source skill SKILL.md)
|
|
93
110
|
- Confidence tier label
|
|
94
111
|
|
|
112
|
+
**If the catalog was extracted** (large stack — step 06 §4 placed the `Library Reference Index` + `Per-Library Summaries` out of SKILL.md), also write `{skill_staging}/references/stack-catalog.md` using the structure in `{stackSkillTemplate}`, and confirm SKILL.md carries the inline pointer instead of the two sections. Small stacks keep the catalog inline and write no `stack-catalog.md`.
|
|
113
|
+
|
|
95
114
|
### 4. Stage Integration Pair Reference Files
|
|
96
115
|
|
|
97
116
|
For each detected integration pair, write `{skill_staging}/references/integrations/{libraryA}-{libraryB}.md`:
|
|
@@ -112,7 +131,7 @@ Write `{skill_staging}/context-snippet.md`:
|
|
|
112
131
|
Use the Vercel-aligned indexed format targeting **~80-120 tokens** (M2). Token estimation is heuristic — use `ceil(char_count / 4)` as the working approximation (the standard rule-of-thumb for English text in BPE-style tokenizers; precise counts differ per model). Compute against the rendered snippet body (excluding trailing newline).
|
|
113
132
|
|
|
114
133
|
```
|
|
115
|
-
[{project_name}-stack v{version —
|
|
134
|
+
[{project_name}-stack v{version — per S11: code-mode primary_library_version or 1.0.0; compose-mode stack-local scheme — 1.0.0 for a new stack, or the bumped {prior_stack_version} on re-composition}]|root: skills/{project_name}-stack/
|
|
116
135
|
|IMPORTANT: {project_name}-stack — read SKILL.md before writing integration code. Do NOT rely on training data.
|
|
117
136
|
|stack: {dep-1}@{v1}, {dep-2}@{v2}, {dep-3}@{v3}
|
|
118
137
|
|integrations: {pattern-1}, {pattern-2}
|
|
@@ -144,7 +163,7 @@ Populate all fields from the metadata.json schema defined in `{stackSkillTemplat
|
|
|
144
163
|
{
|
|
145
164
|
"skill_type": "stack",
|
|
146
165
|
"name": "{project_name}-stack",
|
|
147
|
-
"version": "{primary_library_version or 1.0.0}",
|
|
166
|
+
"version": "{version — resolved per S11: code-mode primary_library_version or 1.0.0; compose-mode 1.0.0 (new stack) or bumped {prior_stack_version} (re-composition)}",
|
|
148
167
|
"generation_date": "{current_date}",
|
|
149
168
|
"forge_tier": "{Quick|Forge|Forge+|Deep — the tier under which this run executed}",
|
|
150
169
|
"confidence_tier": "{T1|T1-low|T2|T3 — dominant T-code from confidence_distribution below}",
|
|
@@ -195,7 +214,7 @@ If any workspace write fails, invoke the rollback contract from §1.
|
|
|
195
214
|
Use the schema from `{provenanceMapSchemaPath}` — see that asset for the canonical templates and field definitions of both variants:
|
|
196
215
|
|
|
197
216
|
- **In code-mode:** use the code-mode variant (`source_repo` / `source_commit` populated; `extraction_method` ∈ `ast_bridge|source_reading|qmd_bridge`; `detection_method = "co-import grep"`).
|
|
198
|
-
- **In compose-mode:** use the compose-mode variant (source-anchor fields `null`; `extraction_method = "compose-from-skill"`; `detection_method ∈ "architecture_co_mention|inferred_from_shared_domain"`; includes the additional `constituents[]` array for drift detection).
|
|
217
|
+
- **In compose-mode:** use the compose-mode variant (source-anchor fields `null`; `extraction_method = "compose-from-skill"`; `detection_method ∈ "architecture_co_mention|constituent_documented_contract|inferred_from_shared_domain"`; includes the additional `constituents[]` array for drift detection).
|
|
199
218
|
|
|
200
219
|
**Use the `metadata_hash` value already stored in workflow state during step 2 (S13) — do NOT re-read and re-hash at step 7 time. The stored hash captures the state as it was at manifest-detection time, which is the correct provenance anchor.**
|
|
201
220
|
|
|
@@ -205,7 +224,28 @@ Use the schema from `{provenanceMapSchemaPath}` — see that asset for the canon
|
|
|
205
224
|
- Warnings and failures encountered
|
|
206
225
|
- Confidence tier distribution
|
|
207
226
|
|
|
208
|
-
### 8. Commit
|
|
227
|
+
### 8. Pre-Commit Frontmatter & Body-Size Gate
|
|
228
|
+
|
|
229
|
+
The full schema/frontmatter validation runs in step 8 (`validate.md`) — but that runs *after* commit-dir and flip-link have already published the package. The two most common non-auto-fixable `skill-check` hard rejects — a `description` over the 1024-char limit (which `skill-check --fix` cannot trim for you) and a SKILL.md body over the `body.max_lines` limit (default **500**) — would therefore only surface on an already-committed, symlink-active artifact, forcing edits to the live `SKILL.md`. Additive re-composition of an already-large stack grows the body monotonically, so the body overflow is the likeliest trigger. Catch both here, while the package is still in `.skf-tmp`.
|
|
230
|
+
|
|
231
|
+
Resolve `{frontmatterValidator}` from `{frontmatterValidatorProbeOrder}` (first existing path wins). Run it against the **staged** `SKILL.md`, passing the real skill name so the directory-match check is not fooled by the `.skf-tmp` staging suffix, and `--max-body-lines 500` to assert the skill-check body limit pre-commit:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
uv run {frontmatterValidator} {skill_staging}/SKILL.md --skill-dir-name {project_name}-stack --max-body-lines 500
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The validator emits JSON: `status` (`pass`/`warn`/`fail`), `issues[]` (each with `severity` ∈ `high|medium|low`, `field`, `message`), `body_lines` (the counted body size), and `summary`. Disposition:
|
|
238
|
+
|
|
239
|
+
- **`status` is `fail`, OR any `issues[]` entry has `severity` `high` or `medium`** — a hard violation that `npx skill-check` (step 8) would reject and `--fix` cannot auto-correct. HALT-to-fix **in staging**, then re-run the validator until it clears. Remediate by `field`:
|
|
240
|
+
- `description` / `name` / `compatibility` — trim/correct `{skill_staging}/SKILL.md` (e.g. shorten `description` to ≤ 1024 chars).
|
|
241
|
+
- `body` (`body lines N exceeds max 500`) — reduce the staged body: prefer a **selective split** of the largest Tier-2 section(s) into `{skill_staging}/references/`, keeping Tier-1 content inline (mirrors `validate.md` §3); or trim redundant content. Re-run the gate until `body_lines ≤ 500`.
|
|
242
|
+
|
|
243
|
+
Do NOT proceed to §9 commit-dir with an unresolved high/medium issue. Note: an over-long `description` is rated `medium` and exits `0`, so key the HALT on the issue severities above — not on the exit code.
|
|
244
|
+
- **Only `low`-severity issues (e.g. an unexpected field)** — record each as a WARNING in the evidence report and proceed; these do not block the commit.
|
|
245
|
+
|
|
246
|
+
**If `{frontmatterValidator}` does not resolve** (neither probe path exists) **or the invocation cannot run**, emit a WARNING ("pre-commit frontmatter + body-size gate skipped — validator unavailable") and proceed. Step 8 (`validate.md`) remains the post-commit backstop (including the `body.max_lines` split path in its §3); this gate is a best-effort early catch, never a new hard dependency.
|
|
247
|
+
|
|
248
|
+
### 9. Commit Staging Directory
|
|
209
249
|
|
|
210
250
|
After all staged writes in sections 2–6 completed successfully, atomically swap the staging dir into place:
|
|
211
251
|
|
|
@@ -215,7 +255,7 @@ python3 {atomicWriteHelper} commit-dir --target {skill_package}
|
|
|
215
255
|
|
|
216
256
|
The helper moves any existing `{skill_package}` aside to a `.skf-rollback-<pid>` dir before the swap. On failure the helper restores the prior target and exits non-zero — in that case invoke the rollback contract from §1 and HALT.
|
|
217
257
|
|
|
218
|
-
###
|
|
258
|
+
### 10. Flip Active Symlink
|
|
219
259
|
|
|
220
260
|
ONLY AFTER `commit-dir` succeeds, flip the `{skill_group}/active` symlink to point at `{version}`:
|
|
221
261
|
|
|
@@ -227,7 +267,7 @@ The helper holds an flock on `{skill_group}/active.skf-lock` and refuses to repl
|
|
|
227
267
|
|
|
228
268
|
If `flip-link` fails, emit a warning (the committed package is still valid), note the symlink-flip failure in the evidence report, and continue.
|
|
229
269
|
|
|
230
|
-
###
|
|
270
|
+
### 11. Display Write Summary
|
|
231
271
|
|
|
232
272
|
"**Output files written.**
|
|
233
273
|
|
|
@@ -248,7 +288,7 @@ If `flip-link` fails, emit a warning (the committed package is still valid), not
|
|
|
248
288
|
|
|
249
289
|
**Proceeding to validation...**"
|
|
250
290
|
|
|
251
|
-
###
|
|
291
|
+
### 12. Auto-Proceed to Next Step
|
|
252
292
|
|
|
253
293
|
Load, read the full file and then execute `{nextStepFile}`.
|
|
254
294
|
|
|
@@ -55,11 +55,11 @@ timeout 10s npx --no-install skill-check -h
|
|
|
55
55
|
|
|
56
56
|
**If available**, run: `npx skill-check check <skill-dir> --fix --format json --no-security-scan`
|
|
57
57
|
|
|
58
|
-
This validates frontmatter, description, body limits, links, formatting — and auto-fixes deterministic issues. Parse JSON for `qualityScore
|
|
58
|
+
This validates frontmatter, description, body limits, links, formatting — and auto-fixes deterministic issues. Parse JSON for `scores[].score` (match the entry by `relativePath`/`skillId`; falls back to a top-level `qualityScore` on older skill-check builds), `diagnostics[]`, `fixed[]`.
|
|
59
59
|
|
|
60
60
|
**Post-fix provenance drift guard (S15):** If `fixed[]` is non-empty, `skill-check --fix` has modified `SKILL.md` after step 7 wrote it. The safe default for v1.0 is to emit a **WARNING** finding listing each auto-fix (`"skill-check --fix modified SKILL.md: {fix_description} — metadata.json hashes/provenance may be out of date"`). Do NOT silently accept the fixes without surfacing the drift. If the caller wants authoritative metadata, they should re-run the workflow.
|
|
61
61
|
|
|
62
|
-
**If `body.max_lines` reported**, prefer selective split: extract only the largest Tier 2 section(s) to `references/`, keeping Tier 1 content inline (inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval). Fall back to `npx skill-check split-body <skill-dir> --write` if not feasible. Verify
|
|
62
|
+
**If `body.max_lines` reported**, prefer selective split: extract only the largest Tier 2 section(s) to `references/`, keeping Tier 1 content inline (inline passive context achieves 100% task accuracy vs 79% for on-demand retrieval). For a stack capstone the canonical split is the catalog (`Library Reference Index` + `Per-Library Summaries`) → `references/stack-catalog.md`, leaving an inline pointer (see `{stackSkillTemplate}` "Sizing Guidance"). This is the **intended** large-stack layout, not a violation: §4 below accepts the pointer form, so clearing the skill-check body ERROR this way does not also trip the structure check. Fall back to `npx skill-check split-body <skill-dir> --write` if not feasible. Verify any in-SKILL.md anchor links (e.g. to the catalog/pointer or other moved sections) still resolve after the split. Then re-validate.
|
|
63
63
|
|
|
64
64
|
**If unavailable**, perform manual frontmatter check:
|
|
65
65
|
- [ ] Frontmatter present with `---` delimiters
|
|
@@ -75,11 +75,14 @@ Load `{stackSkillTemplate}` and verify SKILL.md contains expected sections:
|
|
|
75
75
|
|
|
76
76
|
- [ ] Header with project name, library count, integration count, forge tier
|
|
77
77
|
- [ ] Integration Patterns section (before per-library summaries)
|
|
78
|
-
- [ ] Library Reference Index table
|
|
79
|
-
- [ ] Per-Library Summaries section
|
|
80
78
|
- [ ] Conventions section
|
|
79
|
+
- [ ] **Catalog** — `Library Reference Index` table + `Per-Library Summaries`, in **either** form:
|
|
80
|
+
- *Inline* (small stacks): both sections present in SKILL.md, **or**
|
|
81
|
+
- *Pointer* (large stacks): a `Library Catalog` pointer to `references/stack-catalog.md`, **and** that file exists and contains both sections.
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
Accept either form — do not WARN when the catalog has been extracted to clear the `body.max_lines` budget (§3). Only record a **WARNING** when *neither* the inline sections *nor* a pointer-plus-`stack-catalog.md` is present, or when the pointer's target file is missing.
|
|
84
|
+
|
|
85
|
+
Record other missing sections (Header, Integration Patterns, Conventions) as **WARNING** findings.
|
|
83
86
|
|
|
84
87
|
### 5. Validate metadata.json Fields
|
|
85
88
|
|
|
@@ -79,10 +79,11 @@ For each IDE in `config.yaml.ides`:
|
|
|
79
79
|
Resolve the skill's versioned path before loading artifacts:
|
|
80
80
|
|
|
81
81
|
1. Read `{skills_output_folder}/.export-manifest.json` and look up `{skill-name}` in `exports` to get `active_version`
|
|
82
|
-
2. If
|
|
83
|
-
3. If
|
|
84
|
-
4. If
|
|
85
|
-
5.
|
|
82
|
+
2. **Manifest-lag guard.** If the skill is in the manifest, also read the `active` symlink target at `{skills_output_folder}/{skill-name}/active`. If that symlink resolves to a *different* version than `active_version`, prefer the **symlink target** as `{resolved_version}` and emit an Info note: "manifest active_version {M} lags the active symlink {N} — exporting the symlink target (the just-forged version); the manifest active_version advances to {N} on this export." This is the canonical SS→TS→EX case: create-stack-skill flipped `active` to the new version, but the manifest only advances when *this* export runs. A bare manifest-first resolution would re-export the *previously exported* version, and step 4 §4b/step-5 (`update-context.md`) — which derives the published version from `{resolved_skill_package}/metadata.json` — would then write that stale version straight back as `active_version`, so the forged version could never be published. Resolving to the symlink target here makes this export publish {N} and reconcile the manifest. When the symlink matches `active_version` (or no `active` symlink exists), use `active_version`. See `knowledge/version-paths.md` "Reading Workflows".
|
|
83
|
+
3. If found: resolve to `{skill_package}` = `{skills_output_folder}/{skill-name}/{resolved_version}/{skill-name}/`
|
|
84
|
+
4. If not in manifest: check for `active` symlink at `{skills_output_folder}/{skill-name}/active` — resolve to `{skill_group}/active/{skill-name}/`
|
|
85
|
+
5. If neither: fall back to flat path `{skills_output_folder}/{skill-name}/`. If SKILL.md exists at the flat path, auto-migrate per `knowledge/version-paths.md` migration rules
|
|
86
|
+
6. Store the resolved path as `{resolved_skill_package}` for all subsequent artifact loading
|
|
86
87
|
|
|
87
88
|
Load all files from `{resolved_skill_package}`:
|
|
88
89
|
|
|
@@ -61,7 +61,7 @@ For v1 manifests (no `schema_version` field), the helper migrates in-place on th
|
|
|
61
61
|
3. Wrap in v2 structure: `active_version` ← resolved version, single entry in `versions` with `status: "active"`, `ides: []` (unknown — fills on next successful export), and `last_exported`
|
|
62
62
|
4. Set `schema_version: "2"` at root
|
|
63
63
|
|
|
64
|
-
Workflows that load the manifest via `skf-manifest-ops.py read`
|
|
64
|
+
Workflows that load the manifest via `skf-manifest-ops.py read` receive a `{"status": "ok", "manifest": {...}}` envelope; the `manifest` value is always in canonical v2 shape regardless of on-disk state (parse `result["manifest"]`, not the top-level object).
|
|
65
65
|
|
|
66
66
|
## Integrity invariant
|
|
67
67
|
|
|
@@ -29,7 +29,7 @@ For each artifact, estimate tokens using the heuristic: **words * 1.3** (approxi
|
|
|
29
29
|
3. **SKILL.md** — The full active skill document
|
|
30
30
|
4. **metadata.json** — The machine-readable metadata
|
|
31
31
|
5. **references/** — Total across all reference files (if present)
|
|
32
|
-
6. **Full package total** — Sum of all
|
|
32
|
+
6. **Full package total** — Sum of this skill's own artifacts (context-snippet.md + SKILL.md + metadata.json + references/). **Exclude the Managed section row (item 2)** — it is a shared all-skills cost (it bundles this skill's snippet plus every other skill's snippet), reported separately under Context Budget Impact. Including it would double-count this skill's snippet and fold in unrelated skills' costs.
|
|
33
33
|
|
|
34
34
|
**If passive_context was disabled:** Skip context-snippet.md and managed section measurements, note as "N/A (disabled)".
|
|
35
35
|
|
|
@@ -44,7 +44,7 @@ For each artifact, estimate tokens using the heuristic: **words * 1.3** (approxi
|
|
|
44
44
|
| SKILL.md | {n} | ~{t} | Active skill (on-trigger) |
|
|
45
45
|
| metadata.json | {n} | ~{t} | Machine-readable |
|
|
46
46
|
| references/ | {n} | ~{t} | {count} files |
|
|
47
|
-
| **Package total** | **{n}** | **~{t}** | **
|
|
47
|
+
| **Package total** | **{n}** | **~{t}** | **This skill's own artifacts (snippet + SKILL.md + metadata + references); excludes the shared Managed section row** |
|
|
48
48
|
|
|
49
49
|
**Context Budget Impact:**
|
|
50
50
|
- **Always-on cost:** ~{managed-section-tokens} tokens (managed section in {target-file-list})
|
|
@@ -87,13 +87,13 @@ Read the manifest via `{manifestOpsHelper}` (resolved at §9 from `{manifestOpsP
|
|
|
87
87
|
python3 {manifestOpsHelper} {skills_output_folder} read
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
The helper handles v2-schema enforcement, v1→v2 migration, and the `platforms`→`ides` rename internally. The
|
|
90
|
+
The helper handles v2-schema enforcement, v1→v2 migration, and the `platforms`→`ides` rename internally. The `read` action returns a `{"status": "ok", "manifest": {...}}` envelope — parse `result["manifest"]`, **not** the top-level object (`result["exports"]` raises `KeyError`). The `manifest` value is always in canonical v2 shape regardless of on-disk state.
|
|
91
91
|
|
|
92
92
|
**Schema reference:** `references/manifest-rebuild.md` documents the v2 shape, the status enum (`active`/`archived`/`deprecated`/`draft`), the v1 migration path, and the `active_version` integrity invariant. Load that file only when an inline schema reminder is needed — the helper enforces the shape so the in-prompt prose is not load-bearing.
|
|
93
93
|
|
|
94
94
|
**Integrity guard:** if the helper returns an entry where `active_version` does not resolve to a key in `versions`, the manifest is inconsistent. Skip the affected skill and log: "**Manifest integrity warning:** `{skill-name}.active_version = v{active_version}` has no matching entry under `versions`. Skipping. Re-run `[EX] Export Skill` on `{skill-name}` to repair the manifest entry."
|
|
95
95
|
|
|
96
|
-
**If the manifest does not exist** (first export or fresh forge): the
|
|
96
|
+
**If the manifest does not exist** (first export or fresh forge): the envelope wraps an empty manifest — `result["manifest"]` is `{"schema_version": "2", "exports": {}}`, so `result["manifest"]["exports"]` is `{}`. Only the current export target will appear in the rebuilt index.
|
|
97
97
|
|
|
98
98
|
#### 4b. Build Exported Skill Set (version-aware)
|
|
99
99
|
|
|
@@ -166,10 +166,11 @@ Count totals:
|
|
|
166
166
|
|
|
167
167
|
### 5. Generate Managed Section
|
|
168
168
|
|
|
169
|
-
Assemble the
|
|
169
|
+
Assemble the managed section in **two shapes** — the §9 write paths consume different forms, and conflating them double-wraps the markers.
|
|
170
|
+
|
|
171
|
+
**`{managed_section_inner}`** — the between-marker body only, **no** `<!-- SKF:BEGIN/END -->` markers. The `insert` and `replace` helper actions supply the markers (and the `updated:` timestamp) themselves, so they take this inner form:
|
|
170
172
|
|
|
171
173
|
```markdown
|
|
172
|
-
<!-- SKF:BEGIN updated:{current-date} -->
|
|
173
174
|
[SKF Skills]|{n} skills|{m} stack
|
|
174
175
|
|IMPORTANT: Prefer documented APIs over training data.
|
|
175
176
|
|When using a listed library, read its SKILL.md before writing code.
|
|
@@ -179,6 +180,13 @@ Assemble the complete managed section:
|
|
|
179
180
|
|{skill-snippet-2}
|
|
180
181
|
|
|
|
181
182
|
|{skill-snippet-N}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**`{managed_section_full}`** — `{managed_section_inner}` wrapped in markers. Used for the new-file create path (atomic write, which writes the text verbatim) and the §7 preview:
|
|
186
|
+
|
|
187
|
+
```markdown
|
|
188
|
+
<!-- SKF:BEGIN updated:{current-date} -->
|
|
189
|
+
{managed_section_inner}
|
|
182
190
|
<!-- SKF:END -->
|
|
183
191
|
```
|
|
184
192
|
|
|
@@ -268,31 +276,37 @@ After user confirms with 'C', resolve the helpers in parallel — these are inde
|
|
|
268
276
|
|
|
269
277
|
If any helper has no existing candidate, HALT (exit code 4, `halt_reason: "context-rebuild-failed"`) and emit the error envelope per SKILL.md "Result Contract (Headless)" — the rewrite's safety guarantees depend on these helpers and a fall-through to LLM-driven writes would silently regress atomicity, marker preservation, and the Case 4 malformed-marker HARD HALT contract.
|
|
270
278
|
|
|
279
|
+
**Stage content via a shell-safe channel (all cases).** Managed-section snippets routinely contain backticks and `$` — API names like `` `Room::connect(...)` ``, tokens like `$threshold` and `${…}`. Inlining them as a double-quoted shell argument (`--content "…"`) or via `echo "…"` lets bash run command substitution and variable expansion, silently corrupting the written bytes — and because the helper's byte-identity verify runs against the already-corrupted string, it would still report success. No quoting style is safe (content can also contain single quotes, e.g. `'eager' | 'on-demand'`), so **never** inline the section into the command. Instead write the assembled section to a staging file with your file-write tool (which performs no shell interpretation), then feed it to the helper via stdin redirection — the helper reads stdin whenever `--content` is absent:
|
|
280
|
+
|
|
281
|
+
- For **Cases 2 and 3**, write `{managed_section_inner}` to `{target-file}.skf-content` with **no trailing newline** (the helper appends `\n<!-- SKF:END -->` itself; a trailing newline would insert a blank line before the end marker).
|
|
282
|
+
- For **Case 1**, write `{managed_section_full}` to `{target-file}.skf-content` followed by a single trailing newline (matches the verbatim file-end newline convention).
|
|
283
|
+
- After the helper reports success, delete `{target-file}.skf-content`.
|
|
284
|
+
|
|
271
285
|
For each target context file, dispatch by case:
|
|
272
286
|
|
|
273
287
|
**Case 1 (Create — file does not exist):**
|
|
274
288
|
|
|
275
289
|
```bash
|
|
276
|
-
|
|
290
|
+
python3 {atomicWriteHelper} write --target "{target-file}" < "{target-file}.skf-content"
|
|
277
291
|
```
|
|
278
292
|
|
|
279
|
-
The helper stages the content into `<target>.skf-tmp`, fsyncs, and atomically renames into place.
|
|
293
|
+
The helper stages the content into `<target>.skf-tmp`, fsyncs, and atomically renames into place. This path writes verbatim, so the staging file holds the **marker-bearing** `{managed_section_full}`.
|
|
280
294
|
|
|
281
295
|
**Case 2 (Append — file exists, no `<!-- SKF:BEGIN` marker):**
|
|
282
296
|
|
|
283
297
|
```bash
|
|
284
|
-
python3 {rebuildManagedSectionsHelper} {target-file} insert
|
|
298
|
+
python3 {rebuildManagedSectionsHelper} {target-file} insert < "{target-file}.skf-content"
|
|
285
299
|
```
|
|
286
300
|
|
|
287
|
-
The helper appends the managed section to the end of the file via the same atomic temp-file + rename pattern.
|
|
301
|
+
The helper appends the managed section to the end of the file via the same atomic temp-file + rename pattern. The staging file holds the **inner-only** `{managed_section_inner}` — the helper adds the markers and `updated:` timestamp itself; marker-bearing text double-wraps them.
|
|
288
302
|
|
|
289
303
|
**Case 3 (Regenerate — file contains `<!-- SKF:BEGIN` and `<!-- SKF:END -->`):**
|
|
290
304
|
|
|
291
305
|
```bash
|
|
292
|
-
python3 {rebuildManagedSectionsHelper} {target-file} replace
|
|
306
|
+
python3 {rebuildManagedSectionsHelper} {target-file} replace < "{target-file}.skf-content"
|
|
293
307
|
```
|
|
294
308
|
|
|
295
|
-
The helper performs the surgical between-marker swap with post-write verification (markers present, content outside markers byte-identical).
|
|
309
|
+
The helper performs the surgical between-marker swap with post-write verification (markers present, content outside markers byte-identical). The staging file holds the **inner-only** `{managed_section_inner}` — as with `insert`, the helper supplies the markers; marker-bearing text double-wraps them.
|
|
296
310
|
|
|
297
311
|
**Case 4 (malformed markers — already HALTed in §6):** never reaches here.
|
|
298
312
|
|
|
@@ -338,7 +352,7 @@ On success per file, report: "**{target-file} updated successfully.** Verified b
|
|
|
338
352
|
python3 {manifestOpsHelper} {skills_output_folder} set {skill-name} {version} --ides {ides_written}
|
|
339
353
|
```
|
|
340
354
|
|
|
341
|
-
`{ides_written}` is the comma-joined sorted IDE set computed in step 3. The helper handles v2-schema validation, v1→v2 migration, and `platforms`→`ides` rename internally — no in-prompt JSON manipulation needed. Each `set` invocation unions the supplied `--ides` into the version entry's existing `ides` (deduplicated, sorted) server-side and refreshes `last_exported`. After all skills have been set, re-read the manifest via `{manifestOpsHelper} {skills_output_folder} read` to confirm the final state matches expectations.
|
|
355
|
+
`{ides_written}` is the comma-joined sorted IDE set computed in step 3. The helper handles v2-schema validation, v1→v2 migration, and `platforms`→`ides` rename internally — no in-prompt JSON manipulation needed. Each `set` invocation unions the supplied `--ides` into the version entry's existing `ides` (deduplicated, sorted) server-side and refreshes `last_exported`. After all skills have been set, re-read the manifest via `{manifestOpsHelper} {skills_output_folder} read` (the v2 manifest is under the `manifest` key of the returned envelope — see §4a) to confirm the final state matches expectations.
|
|
342
356
|
|
|
343
357
|
**Dry-run mode:** Do NOT update the manifest. Display: "**[DRY RUN] Export manifest would be updated for {skill-name-list} — ides: {ides_written}.**" (list every skill in `skill_batch`)
|
|
344
358
|
|
|
@@ -54,9 +54,9 @@ Indexed format targeting ~80-120 tokens per skill:
|
|
|
54
54
|
```markdown
|
|
55
55
|
[{skill_name} v{version}]|root: skills/{skill_name}/
|
|
56
56
|
|IMPORTANT: {skill_name} v{version} — read SKILL.md before writing {skill_name} code. Do NOT rely on training data.
|
|
57
|
-
|quick-start:{SKILL.md#
|
|
57
|
+
|quick-start:{SKILL.md#usage-patterns}
|
|
58
58
|
|api: {top-5 exports with () for functions}
|
|
59
|
-
|key-types:{SKILL.md#key-
|
|
59
|
+
|key-types:{SKILL.md#key-exports} — {inline summary of most important type values}
|
|
60
60
|
|gotchas: {2-3 most critical pitfalls or breaking changes, inline}
|
|
61
61
|
```
|
|
62
62
|
|
|
@@ -75,12 +75,14 @@ Otherwise, create context-snippet.md in Vercel-aligned indexed format (~80-120 t
|
|
|
75
75
|
```
|
|
76
76
|
[{skill_name} v{version}]|root: skills/{skill_name}/
|
|
77
77
|
|IMPORTANT: {skill_name} v{version} — read SKILL.md before writing {skill_name} code. Do NOT rely on training data.
|
|
78
|
-
|quick-start:{SKILL.md#
|
|
78
|
+
|quick-start:{SKILL.md#usage-patterns}
|
|
79
79
|
|api: {top-5 exports with () for functions}
|
|
80
|
-
|key-types:{SKILL.md#key-
|
|
80
|
+
|key-types:{SKILL.md#key-exports} — {inline summary of most important type values}
|
|
81
81
|
|gotchas: {2-3 most critical pitfalls or breaking changes, inline}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
The anchors point to the QS template's actual headings — `#usage-patterns` (Usage Patterns) and `#key-exports` (Key Exports). The QS template has no `## Quick Start` / `## Key Types` headings (those are Deep-tier sections), so the Deep-tier anchors `#quick-start` / `#key-types` would dangle. If the assembled SKILL.md is missing the referenced heading, omit that line rather than emit a dangling anchor.
|
|
85
|
+
|
|
84
86
|
**If fewer than 5 exports:** Use all available exports.
|
|
85
87
|
**If no exports:** Omit the api line.
|
|
86
88
|
**If no gotchas known:** Omit the gotchas line.
|
|
@@ -84,7 +84,7 @@ npx skill-check check {skill_package} --fix --format json
|
|
|
84
84
|
This validates frontmatter, description, body limits, links, and formatting; runs the security scan; and auto-fixes deterministic issues (field ordering, slug format, required fields, trailing newlines).
|
|
85
85
|
|
|
86
86
|
**Parse JSON output** to extract:
|
|
87
|
-
- `
|
|
87
|
+
- `scores[].score` — overall score (0-100); match the entry by `relativePath`/`skillId` (older skill-check builds exposed this as a top-level `qualityScore`)
|
|
88
88
|
- `diagnostics[]` — remaining issues after auto-fix
|
|
89
89
|
- `fixed[]` — issues automatically corrected
|
|
90
90
|
- `security[]` (when present) — security findings, recorded as advisory warnings (security issues do not block output)
|
|
@@ -94,7 +94,7 @@ Record quality score, remaining diagnostics, and security findings as validation
|
|
|
94
94
|
**If skill-check is NOT available**, run the shared frontmatter validator instead of an LLM-walked checklist. Resolve `{frontmatterValidator}` from `{frontmatterValidatorProbeOrder}`; first existing path wins. If no candidate exists, log a high-severity issue ("frontmatter validator unavailable — both `npx skill-check` and `skf-validate-frontmatter.py` missing") and skip frontmatter validation.
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
|
|
97
|
+
uv run {frontmatterValidator} {skill_package}/SKILL.md --skill-dir-name {repo_name}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
The validator emits JSON with `status` (`pass`/`fail`), `issues[]` (each with `severity`, `code`, `message`), and `frontmatter` (the parsed name/description). It checks frontmatter delimiters, name format (Unicode letters + digits + hyphens, no consecutive/trailing hyphens), name-directory match, description presence and length, and unknown fields against the agentskills.io spec — the same shape this step would otherwise hand-walk. Record each `issues[]` entry as a validation issue with its reported severity. Missing frontmatter or missing required fields are high-severity — skills without valid frontmatter will fail `npx skills add` and `npx skill-check check`.
|