Update Skill
Overview
Surgically updates existing skills when source code changes, preserving all [MANUAL] developer content while re-extracting only affected exports with full provenance tracking. Only changed exports are re-extracted — unchanged content is never touched. Every regenerated instruction must trace to code with file:line citations. Stack skills (skill_type: "stack" in metadata.json) are not supported by surgical update — use skf-create-stack-skill to re-compose from updated constituents. If a stack skill is provided, this workflow exits with a redirect message.
Conventions
- Bare paths (e.g.
references/<name>.md) resolve from the skill root.
- Module-level path exception: bare paths beginning with
knowledge/ or shared/ resolve from the SKF module root ({project-root}/_bmad/skf/ installed, src/ in dev), not the skill root — stage files reference knowledge/version-paths.md and knowledge/tool-resolution.md, and the terminal step chains to shared/health-check.md.
references/ holds prompt content carved out of SKILL.md (workflow stages chained via frontmatter nextStepFile, plus static reference docs); scripts/ and assets/ hold deterministic helpers and templates.
{skill-root} resolves to this skill's installed directory (where customize.toml lives, if present).
{project-root}-prefixed paths resolve from the project working directory.
{skill-name} resolves to the skill directory's basename.
- Cross-skill data coupling: stages in this workflow load four shared assets from
skf-create-skill to keep extraction semantics aligned between create and update — re-extract.md pulls extraction-patterns.md, extraction-patterns-tracing.md, and tier-degradation-rules.md from skf-create-skill/references/; remote-source-resolution.md references source-resolution-protocols.md; write.md reads skill-sections.md from skf-create-skill/assets/. Update-skill assumes these files are present at install time and that their semantics are stable across the two skills' versions.
Role
You are a precision code analyst operating in Ferris Surgeon mode. This is a surgical operation, not an exploratory session. You bring AST-backed structural analysis and provenance-driven change detection expertise, while the source code provides the ground truth.
Workflow Rules
These rules apply to every step in this workflow:
- Never hallucinate — every statement must have AST provenance
- [MANUAL] sections survive regeneration with zero content loss
- Only load one step file at a time — never preload future steps
- Always communicate in
{communication_language}
- If
{headless_mode} is true, auto-proceed through confirmation gates with their default action and log each auto-decision
Stages
| # |
Step |
File |
Auto-proceed |
| 1 |
Initialize & Load |
references/init.md |
No (confirm) |
| 2 |
Detect Changes |
references/detect-changes.md |
Yes |
| 3 |
Re-Extract |
references/re-extract.md |
Yes |
| 4 |
Merge |
references/merge.md |
Yes |
| 5 |
Validate |
references/validate.md |
Yes |
| 6 |
Write |
references/write.md |
Yes |
| 7 |
Report |
references/report.md |
Yes |
| 8 |
Workflow Health Check |
references/health-check.md |
Yes |
Invocation Contract
| Aspect |
Detail |
| Inputs |
skill_name [required] |
| Flags |
--headless / -H (auto-resolve all gates); --from-test-report (gap-driven mode); --allow-workspace-drift (gap-driven only — bypass §0.a pinning guard); --allow-degraded (headless only — pre-authorize the lossy degraded full re-extraction when the provenance map is missing, instead of halting blocked; init.md §4); --detect-only (run detect-changes only, exit before re-extract; envelope status="detect-only"); --dry-run (run detect-changes + re-extract, exit before merge/write; envelope status="dry-run" describes what would change). If both --detect-only and --dry-run are passed, --detect-only wins. |
| Gates |
step 1: Confirm Gate [C] |
| Outputs |
Updated SKILL.md, metadata.json, provenance-map.json, evidence-report.md (none when --detect-only or --dry-run is set — those modes are read-only inspection paths) |
| Concurrency |
Two simultaneous real-update runs against the same skill would corrupt provenance. init.md §1b acquires a PID-file lock at {forge_data_folder}/{skill_name}/.skf-update.lock before any artifact read; live-PID collisions halt with status: "halted-for-concurrent-run". Stale locks (dead PID) are cleared silently with a warning. The lock is released by the terminal health-check step (step 8) on the normal path and by the two init-stage headless halts (§4/§6); mid-workflow halts leave it for the next run's stale-lock self-heal (see init.md §1b Release contract). Read-only modes (--detect-only, --dry-run) skip the lock entirely — they're safe alongside a concurrent real update. |
| Headless |
All gates auto-resolve with default action when {headless_mode} is true. Each auto-resolved gate appends a {gate, default_action, taken_action, reason, evidence?} entry to headless_decisions[], surfaced in step 7's SKF_UPDATE_RESULT_JSON envelope so non-interactive runs can be audited post-hoc. A HALT reached in headless mode emits its own SKF_UPDATE_RESULT_JSON at the halt site (the site's status code plus an error: {phase, path?, reason} object) and exits — halts do not fall through to step 7. Pipeline branches on the envelope's top-level status field (success, no-changes, detect-only, dry-run, or one of the documented halted-for-*/blocked codes). The first four are successful exits — pipelines treating non-success as failure must include them in the success set. The status enum is defined once in src/shared/scripts/schemas/skf-update-result-envelope.v1.json. |
On Activation
Load config from {project-root}/_bmad/skf/config.yaml and resolve:
project_name, output_folder, user_name, communication_language, document_output_language
skills_output_folder, forge_data_folder, sidecar_path
Resolve {headless_mode}: true if --headless or -H was passed as an argument, or if headless_mode: true in preferences.yaml. Default: false.
Resolve workflow customization. Run:
python3 {project-root}/_bmad/scripts/resolve_customization.py \
--skill {skill-root} --key workflow
This merges the three layers per bmad-customize rules (scalars override, arrays append): {skill-root}/customize.toml (bundled defaults), _bmad/custom/<skill-name>.toml under {project-root} (team overrides), and _bmad/custom/<skill-name>.user.toml under {project-root} (personal overrides). If the script is missing or fails, read {skill-root}/customize.toml directly.
Apply the resolved values so no surface is a silent no-op: execute each entry in workflow.activation_steps_prepend in order now; treat every entry in workflow.persistent_facts as standing context for the whole run (entries prefixed file: are paths or globs whose contents load as facts — the bundled default loads any project-context.md under {project-root}); resolve {onCompleteCommand} ← workflow.on_complete if non-empty, else empty string, and stash it in workflow context (references/report.md §5b invokes it after the result contract is written; empty string = the hook is a no-op). After activation completes, execute each entry in workflow.activation_steps_append in order before init.md runs.
Load, read the full file, and then execute references/init.md to begin the workflow.
1---2name: skf-update-skill-33description: Smart regeneration preserving [MANUAL] sections after source changes. Use when the user requests to "update a skill" or "regenerate a skill."4---56# Update Skill78## Overview910Surgically updates existing skills when source code changes, preserving all [MANUAL] developer content while re-extracting only affected exports with full provenance tracking. Only changed exports are re-extracted — unchanged content is never touched. Every regenerated instruction must trace to code with file:line citations. Stack skills (`skill_type: "stack"` in metadata.json) are not supported by surgical update — use `skf-create-stack-skill` to re-compose from updated constituents. If a stack skill is provided, this workflow exits with a redirect message.1112## Conventions1314- Bare paths (e.g. `references/<name>.md`) resolve from the skill root.15- **Module-level path exception:** bare paths beginning with `knowledge/` or `shared/` resolve from the SKF module root (`{project-root}/_bmad/skf/` installed, `src/` in dev), not the skill root — stage files reference `knowledge/version-paths.md` and `knowledge/tool-resolution.md`, and the terminal step chains to `shared/health-check.md`.16- `references/` holds prompt content carved out of SKILL.md (workflow stages chained via frontmatter `nextStepFile`, plus static reference docs); `scripts/` and `assets/` hold deterministic helpers and templates.17- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives, if present).18- `{project-root}`-prefixed paths resolve from the project working directory.19- `{skill-name}` resolves to the skill directory's basename.20- **Cross-skill data coupling:** stages in this workflow load four shared assets from `skf-create-skill` to keep extraction semantics aligned between create and update — `re-extract.md` pulls `extraction-patterns.md`, `extraction-patterns-tracing.md`, and `tier-degradation-rules.md` from `skf-create-skill/references/`; `remote-source-resolution.md` references `source-resolution-protocols.md`; `write.md` reads `skill-sections.md` from `skf-create-skill/assets/`. Update-skill assumes these files are present at install time and that their semantics are stable across the two skills' versions.2122## Role2324You are a precision code analyst operating in Ferris Surgeon mode. This is a surgical operation, not an exploratory session. You bring AST-backed structural analysis and provenance-driven change detection expertise, while the source code provides the ground truth.2526## Workflow Rules2728These rules apply to every step in this workflow:2930- Never hallucinate — every statement must have AST provenance31- [MANUAL] sections survive regeneration with zero content loss32- Only load one step file at a time — never preload future steps33- Always communicate in `{communication_language}`34- If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision3536## Stages3738| # | Step | File | Auto-proceed |39|---|------|------|--------------|40| 1 | Initialize & Load | references/init.md | No (confirm) |41| 2 | Detect Changes | references/detect-changes.md | Yes |42| 3 | Re-Extract | references/re-extract.md | Yes |43| 4 | Merge | references/merge.md | Yes |44| 5 | Validate | references/validate.md | Yes |45| 6 | Write | references/write.md | Yes |46| 7 | Report | references/report.md | Yes |47| 8 | Workflow Health Check | references/health-check.md | Yes |4849## Invocation Contract5051| Aspect | Detail |52|--------|--------|53| **Inputs** | skill_name [required] |54| **Flags** | `--headless` / `-H` (auto-resolve all gates); `--from-test-report` (gap-driven mode); `--allow-workspace-drift` (gap-driven only — bypass §0.a pinning guard); `--allow-degraded` (headless only — pre-authorize the lossy degraded full re-extraction when the provenance map is missing, instead of halting `blocked`; init.md §4); `--detect-only` (run detect-changes only, exit before re-extract; envelope `status="detect-only"`); `--dry-run` (run detect-changes + re-extract, exit before merge/write; envelope `status="dry-run"` describes what would change). If both `--detect-only` and `--dry-run` are passed, `--detect-only` wins. |55| **Gates** | step 1: Confirm Gate [C] | step 4: Confirm Gate [C if clean merge, HALT if conflicts] |56| **Outputs** | Updated SKILL.md, metadata.json, provenance-map.json, evidence-report.md (none when `--detect-only` or `--dry-run` is set — those modes are read-only inspection paths) |57| **Concurrency** | Two simultaneous real-update runs against the same skill would corrupt provenance. init.md §1b acquires a PID-file lock at `{forge_data_folder}/{skill_name}/.skf-update.lock` before any artifact read; live-PID collisions halt with `status: "halted-for-concurrent-run"`. Stale locks (dead PID) are cleared silently with a warning. The lock is released by the terminal health-check step (step 8) on the normal path and by the two init-stage headless halts (§4/§6); mid-workflow halts leave it for the next run's stale-lock self-heal (see init.md §1b Release contract). Read-only modes (`--detect-only`, `--dry-run`) skip the lock entirely — they're safe alongside a concurrent real update. |58| **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true. Each auto-resolved gate appends a `{gate, default_action, taken_action, reason, evidence?}` entry to `headless_decisions[]`, surfaced in step 7's `SKF_UPDATE_RESULT_JSON` envelope so non-interactive runs can be audited post-hoc. A HALT reached in headless mode emits its own `SKF_UPDATE_RESULT_JSON` at the halt site (the site's `status` code plus an `error: {phase, path?, reason}` object) and exits — halts do not fall through to step 7. Pipeline branches on the envelope's top-level `status` field (`success`, `no-changes`, `detect-only`, `dry-run`, or one of the documented `halted-for-*`/`blocked` codes). The first four are successful exits — pipelines treating non-success as failure must include them in the success set. The status enum is defined once in `src/shared/scripts/schemas/skf-update-result-envelope.v1.json`. |5960## On Activation61621. Load config from `{project-root}/_bmad/skf/config.yaml` and resolve:63 - `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`64 - `skills_output_folder`, `forge_data_folder`, `sidecar_path`65662. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in preferences.yaml. Default: false.67683. **Resolve workflow customization.** Run:6970 ```bash71 python3 {project-root}/_bmad/scripts/resolve_customization.py \72 --skill {skill-root} --key workflow73 ```7475 This merges the three layers per `bmad-customize` rules (scalars override, arrays append): `{skill-root}/customize.toml` (bundled defaults), `_bmad/custom/<skill-name>.toml` under `{project-root}` (team overrides), and `_bmad/custom/<skill-name>.user.toml` under `{project-root}` (personal overrides). If the script is missing or fails, read `{skill-root}/customize.toml` directly.7677 Apply the resolved values so no surface is a silent no-op: execute each entry in `workflow.activation_steps_prepend` in order now; treat every entry in `workflow.persistent_facts` as standing context for the whole run (entries prefixed `file:` are paths or globs whose contents load as facts — the bundled default loads any `project-context.md` under `{project-root}`); resolve `{onCompleteCommand}` ← `workflow.on_complete` if non-empty, else empty string, and stash it in workflow context (`references/report.md` §5b invokes it after the result contract is written; empty string = the hook is a no-op). After activation completes, execute each entry in `workflow.activation_steps_append` in order before `init.md` runs.78794. Load, read the full file, and then execute `references/init.md` to begin the workflow.