# Skill Lifecycle Management

> Manages the full lifecycle of OpenCode AI skills including versioning strategies, deprecation workflows, backward compatibility checks, and retirement procedures for the agent-skill-router system.

- Skill: `paulpas/skill-lifecycle-management` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/skill-lifecycle-management`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/skill-lifecycle-management/raw
- Safety review: CAUTION (external: skill-scanner PASS, skillspector FAIL)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: paulpas (https://skillmd.com/u/paulpas)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/paulpas/skill-lifecycle-management

---





# Skill Lifecycle Manager

Orchestrates the complete lifecycle of OpenCode AI skills from initial creation through versioning, deprecation, backward compatibility validation, and retirement — ensuring every skill evolves without breaking existing auto-routing pipelines.

## TL;DR Checklist

- [ ] Parse SKILL.md frontmatter and extract current version before any change
- [ ] Classify the change type (MAJOR / MINOR / PATCH) using semantic versioning rules
- [ ] Update `metadata.version` in frontmatter to match the classified bump
- [ ] Run backward compatibility check against all skills listing this one in `related-skills`
- [ ] If deprecating, write a migration guide and update related-skills cross-references
- [ ] Regenerate README catalog with `python3 scripts/generate_readme.py`

---

## When to Use

Use this skill when:

- You need to bump the version of an existing skill after making changes
- Planning to remove trigger terms, workflow steps, or frontmatter fields from a skill
- A skill has fallen out of use and needs deprecation or retirement procedures
- You are introducing a breaking change and need to communicate it to dependent skills
- Writing a migration guide for users affected by a skill's structural changes

---

## When NOT to Use

Avoid this skill for:

- Initial creation of a brand-new skill (use `skill-engineering` instead)
- Auditing the quality of existing skills (use `skill-audit` instead)
- Routing tasks to skills or managing the routing index itself (use `skill-router-system` instead)
- Simple typo fixes that do not change behavior — just commit with a PATCH version bump

---

## Core Workflow

```
┌──────────────┐    ┌───────────────┐    ┌───────────────┐
│   Idea       │    │   Design      │    │   Create      │
│   & Scope    │───▶│   Spec +     │───▶│   SKILL.md    │
│              │    │   Schema      │    │               │
└──────────────┘    └───────────────┘    └───────┬───────┘
                                                   │
┌──────────────┐    ┌───────────────┐    ┌─────────▼─────────┐
│   Retire     │◀───│  Deprecate    │◀───│   Deploy &        │
│   (archive)  │    │  (mark +      │    │   Validate        │
│              │    │   migrate)     │    │                   │
└──────────────┘    └───────────────┘    └─────────┬─────────┘
                                                   │
                                    ┌──────────────▼─────────┐
                                    │   Monitor &            │
                                    │   Version Bump         │
                                    └────────────────────────┘
```

1. **Classify the Change** — Determine whether the change to a skill is MAJOR, MINOR, or PATCH based on the semantic versioning rules below. Read the current `metadata.version` field, then decide the next version number.
   **Checkpoint:** Record the old version (e.g., `1.2.3`) and the new version (e.g., `1.3.0` for MINOR) in a changelog comment block at the top of SKILL.md.

2. **Bump the Version** — Edit the frontmatter `metadata.version` field to match the classified bump. Also increment the corresponding section count in the file (add/remove patterns, workflow steps).
   **Checkpoint:** Run `./scripts/validate_skill.sh <path>` to verify frontmatter is still valid YAML after the edit.

3. **Run Backward Compatibility Check** — Execute a cross-reference scan: find every other skill whose `related-skills` field lists this skill by name. Verify those skills' trigger sets will not break if this skill's triggers change.
   **Checkpoint:** No dependent skill should list a removed trigger term. If one does, either preserve the trigger or update the dependent skill first.

4. **Update Cross-References** — For every skill that lists this skill as related, verify the path is still correct (directory exists, frontmatter name matches). Remove any entries pointing to archived skills.
   **Checkpoint:** `grep -r "name: <this-skill>" skills/*/SKILL.md` should return only active skills with correct paths.

5. **Regenerate Documentation** — Run `python3 scripts/generate_readme.py` so the README skill catalog reflects any renamed, deprecated, or newly versioned entries.
   **Checkpoint:** Verify your skill appears in the README under its domain section with the updated description and version note.

6. **Commit With Version Tag** — Create a commit that includes only the version bump and any dependency updates: `git add skills/<domain>/<topic>/SKILL.md scripts/generate_readme.py`. Do not include unrelated changes in a version-only commit.
   **Checkpoint:** `git diff --cached` should show no unexpected files beyond the skill itself and the generated README.

---

## Implementation Patterns

### Versioning Strategy — Semantic Versioning Rules

Apply semantic versioning (MAJOR.MINOR.PATCH) to every skill. The rules are strict: a MAJOR bump means breaking changes, MINOR means additive changes, PATCH means non-functional edits.

```python
from enum import Enum


class VersionBumpType(Enum):
    MAJOR = "major"
    MINOR = "minor"
    PATCH = "patch"


def classify_version_bump(
    old_version: str,
    change_type: str
) -> tuple[str, str]:
    """Classify a version bump and compute the next version string.

    Args:
        old_version: Current semantic version (e.g., "1.2.3").
        change_type: One of 'major', 'minor', or 'patch'.

    Returns:
        Tuple of (bump_classification, new_version_string).

    Raises:
        ValueError: If old_version is not a valid semver string
                    or change_type is not one of the allowed values.
    """
    parts = old_version.split(".")
    if len(parts) != 3:
        raise ValueError(
            f"Expected semver format MAJOR.MINOR.PATCH, got: {old_version}"
        )

    try:
        major, minor, patch = [int(p) for p in parts]
    except ValueError:
        raise ValueError(f"Version parts must be integers: {old_version}")

    if change_type == "major":
        new_version = f"{major + 1}.0.0"
    elif change_type == "minor":
        new_version = f"{major}.{minor + 1}.0"
    elif change_type == "patch":
        new_version = f"{major}.{minor}.{patch + 1}"
    else:
        raise ValueError(
            f"Invalid change_type '{change_type}'. Use 'major', 'minor', or 'patch'."
        )

    return (change_type, new_version)


# Examples of classification:
# classify_version_bump("1.2.3", "patch") → ("patch", "1.2.4")
# classify_version_bump("1.2.3", "minor") → ("minor", "1.3.0")
# classify_version_bump("1.2.3", "major") → ("major", "2.0.0")
```

**Rules for classification:**

| Bump Type | What Changes | Example Scenario |
|-----------|-------------|-----------------|
| MAJOR | Trigger set changed, core workflow steps removed or reordered, frontmatter schema changed | Removing `stop loss` as a trigger after migrating to a new skill |
| MINOR | New patterns added, expanded examples, additional trigger terms, new sub-sections | Adding an ATR-based stop pattern to the existing stop-loss skill |
| PATCH | Typo fixes in prose, minor wording clarifications, code comment updates | Fixing a misspelled variable name in a docstring |

---

### Deprecation Workflow

When a skill should no longer be actively developed but must remain available for existing auto-routing:

```python
import os
import re
from dataclasses import dataclass


@dataclass
class DeprecationRecord:
    """Records the metadata of a deprecation event."""
    skill_name: str
    deprecated_by: str
    reason: str
    replacement_skill: str | None
    effective_date: str  # ISO 8601: "2026-05-19"
    sunset_date: str | None  # Optional future date for full retirement


def mark_deprecated(skill_path: str, record: DeprecationRecord) -> list[str]:
    """Mark a skill as deprecated by updating SKILL.md in place.

    Returns a list of file paths that were modified (includes cross-refs).

    Args:
        skill_path: Absolute path to the SKILL.md to deprecate.
        record: DeprecationRecord with full context.
    """
    with open(skill_path, "r") as f:
        content = f.read()

    lines = content.splitlines(True)

    # Insert DEPRECATED banner after H1 title
    modified = []
    for i, line in enumerate(lines):
        if line.startswith("# ") and not line.startswith("##"):
            modified.append(line)  # Keep the H1
            modified.append(f"\n<!-- ⚠️ DEPRECATED: {record.reason} -->\n")
            modified.append(
                f"<!-- Replaced by: `{record.replacement_skill}` -->\n"
            )
            modified.append("<!-- Deprecated: " + record.effective_date + " -->\n")
            continue
        modified.append(line)

    new_content = "".join(modified)

    # Verify the deprecation banner was inserted
    if "⚠️ DEPRECATED" not in new_content:
        raise RuntimeError("Failed to insert deprecation banner into SKILL.md")

    with open(skill_path, "w") as f:
        f.write(new_content)

    modified_files = [skill_path]

    # Update any related-skills entries that point here
    for skill_file in _find_related_skills(skill_path):
        updated = _update_related_reference(skill_file, record.replacement_skill)
        if updated:
            modified_files.append(skill_file)

    return modified_files


def write_migration_guide(
    deprecated_skill: str,
    replacement_skill: str,
    output_dir: str
) -> str:
    """Write a migration guide for users affected by the deprecation.

    Returns the path to the generated migration guide file.
    """
    guide_path = os.path.join(output_dir, f"migration-{deprecated_skill}.md")

    with open(guide_path, "w") as f:
        f.write(f"# Migration Guide: {deprecated_skill} → {replacement_skill}\n\n")
        f.write("## Why This Change Happened\n\n")
        f.write("This skill was deprecated because its functionality has been\n")
        f.write("consolidated into a newer, more comprehensive skill.\n\n")
        f.write("## What Changes for You\n\n")
        f.write(f"1. Replace any `{deprecated_skill}` trigger references with\n")
        f.write(f"   `{replacement_skill}` triggers.\n")
        f.write(f"2. Update your `related-skills` field to point to\n")
        f.write(f"   `{replacement_skill}` instead of `{deprecated_skill}`.\n")
        f.write(f"3. Review the [SKILL.md for {replacement_skill}]({replacement_skill}/SKILL.md)\n")
        f.write(f"   for updated workflow steps.\n\n")
        f.write("## Timeline\n\n")
        f.write("- **Today:** Skill marked deprecated, auto-routing still active\n")
        f.write("- **30 days:** Auto-routing gradually shifts to replacement skill\n")
        f.write("- **90 days:** Deprecated skill removed from skills-index.json\n")

    return guide_path


def _find_related_skills(current_skill_path: str) -> list[str]:
    """Find all SKILL.md files that reference the given skill in related-skills."""
    base_dir = os.path.dirname(os.path.dirname(os.path.dirname(current_skill_path)))
    results = []

    for domain_dir in os.listdir(base_dir):
        domain_path = os.path.join(base_dir, domain_dir)
        if not os.path.isdir(domain_path):
            continue
        for skill_dir in os.listdir(domain_path):
            skill_file = os.path.join(domain_path, skill_dir, "SKILL.md")
            if not os.path.isfile(skill_file):
                continue
            with open(skill_file, "r") as f:
                content = f.read()
            # Check for the skill name in related-skills field
            if f"{current_skill_path.split('/')[-2]}" in content:
                results.append(skill_file)

    return results


def _update_related_reference(
    skill_file: str, replacement_skill: str | None
) -> bool:
    """Update a cross-reference to point to the replacement skill."""
    with open(skill_file, "r") as f:
        content = f.read()

    # Simple replacement of related-skills entries
    old_name = skill_file.split("/")[-2]
    new_content = content.replace(old_name, replacement_skill or old_name)

    if old_name != (replacement_skill or ""):
        with open(skill_file, "w") as f:
            f.write(new_content)
        return True

    return False
```

---

### Backward Compatibility Checker

Before publishing a version change, verify that no dependent skills break. This function scans the entire skills directory for cross-reference integrity.

```python
import yaml


def check_backward_compatibility(skill_path: str, base_dir: str) -> dict:
    """Check backward compatibility before publishing a skill change.

    Args:
        skill_path: Path to the SKILL.md being modified.
        base_dir: Root directory of the skills/ folder.

    Returns:
        Compatibility report with issues and recommendations.
    {
        "compatible": True,
        "issues": [],
        "recommendations": []
    }
    """
    report = {"compatible": True, "issues": [], "recommendations": []}

    with open(skill_path, "r") as f:
        data = yaml.safe_load(f)

    old_triggers = set(
        t.strip()
        for t in data.get("metadata", {}).get("triggers", "").split(",")
    )
    skill_name = data["name"]

    # Scan all other skills for references to this one
    for root, dirs, files in os.walk(base_dir):
        if "SKILL.md" not in files:
            continue
        other_path = os.path.join(root, "SKILL.md")
        if other_path == skill_path:
            continue

        with open(other_path, "r") as f:
            other_data = yaml.safe_load(f)

        related = [
            s.strip()
            for s in (other_data.get("metadata", {}).get("related-skills") or "").split(",")
        ]

        if skill_name not in related:
            continue

        # This skill lists us as related — verify no trigger overlap issues
        other_triggers = set(
            t.strip()
            for t in (other_data.get("metadata", {}).get("triggers") or "").split(",")
        )

        # If we removed a trigger that the other skill uses
        removed_triggers = old_triggers - new_triggers  # type: ignore[name-defined]
        if removed_triggers & other_triggers:
            report["compatible"] = False
            report["issues"].append(
                f"Trigger(s) {removed_triggers} were removed but "
                f"'{other_data['name']}' still uses them."
            )
            report["recommendations"].append(
                f"Add back '{', '.join(removed_triggers)}' as a trigger or "
                f"update '{other_data['name']}' to use the replacement skill."
            )

    return report


# Usage example:
# report = check_backward_compatibility("skills/trading/risk-stop-loss/SKILL.md", "skills/")
# if not report["compatible"]:
#     for issue in report["issues"]:
#         print(f"ISSUE: {issue}")
#     for rec in report["recommendations"]:
#         print(f"FIX: {rec}")
```

---

### Retirement and Sunset Procedure

When a skill reaches end-of-life, follow this archive procedure:

```yaml
# Step 1: Move to .archive/ directory structure
# skills/ -> .archive/skills/<domain>/<topic>/
# Preserve the original SKILL.md with DEPRECATED banner intact

# Step 2: Update skills-index.json
# - Remove the skill from the active index
# - Add an "archived" entry under a new "archived_skills" array
#   {
#     "name": "old-skill-name",
#     "domain": "agent",
#     "archived_date": "2026-05-19",
#     "replaced_by": "new-skill-name",
#     "reason": "functionality superseded by new skill"
#   }

# Step 3: Update README.md catalog
# - Remove from the active skill count per domain
# - Add a small "Archived Skills" section at the bottom with links to .archive/

# Step 4: Verify dependent skills no longer reference the archived one
```

**When to retire a skill:**
- **Low usage** — The router access log shows fewer than 10 matches per month for 6 consecutive months
- **Superseded** — A newer skill covers the same domain with better content and triggers
- **Domain moved** — The topic no longer belongs in its current domain (e.g., a trading risk pattern that evolved into a general coding pattern)

**Sunset timeline:**
- **Day 0:** Mark deprecated, publish migration guide
- **Day +30:** Stop indexing as auto-loadable, keep manual access via `/skill`
- **Day +90:** Archive to `.archive/`, remove from skills-index.json active list
- **Day +365:** Purge archived copy (or keep indefinitely if requested)

---

### BAD vs GOOD Examples Across Lifecycle Stages

#### Versioning — BAD vs GOOD

```python
# ❌ BAD: Bumping version without classifying the change type
version = "1.2.3"
new_version = version + ".1"  # Random string concatenation, no semantics

# ✅ GOOD: Using the classification function with explicit rationale
old_ver = "1.2.3"
bump_type, new_ver = classify_version_bump(old_ver, "minor")
assert new_ver == "1.3.0", f"Expected 1.3.0, got {new_ver}"
# Rationale: Added a new ATR-based pattern — additive, non-breaking → MINOR
```

#### Deprecation — BAD vs GOOD

```markdown
<!-- ❌ BAD: No banner, no migration path, just delete the file -->
rm skills/agent/old-skill/SKILL.md

<!-- ✅ GOOD: Mark deprecated with banner and migration guide -->
# Old Skill Name
<!-- ⚠️ DEPRECATED: Functionality merged into skill-engineering -->
<!-- Replaced by: `skill-engineering` -->
<!-- Deprecated: 2026-05-19 -->
```

#### Cross-Reference Integrity — BAD vs GOOD

```yaml
# ❌ BAD: Related-skills references a directory that does not exist
related-skills: skill-engineering, phantom-nonexistent-skill, coding-code-review

# ✅ GOOD: All referenced skills exist and reciprocate
related-skills: skill-engineering, skill-router-system, skill-audit
```

---

## Constraints

### MUST DO
- Classify every version bump as MAJOR, MINOR, or PATCH before editing frontmatter — never guess
- Write a migration guide whenever deprecating a skill whose triggers overlap with other active skills
- Run backward compatibility checks against the full skills/ tree before publishing any MAJOR change
- Update `skills-index.json` and regenerate README.md after every version change
- Maintain the archive for at least 90 days after sunset date to allow dependent tools to adjust

### MUST NOT DO
- Delete a deprecated skill without first archiving it — users may still load it manually via `/skill <name>`
- Change trigger terms without running the backward compatibility checker first
- Bump only the PATCH version when removing workflow steps or patterns (that is at minimum a MINOR change)
- Leave orphaned entries in other skills' `related-skills` fields after retirement
- Use semantic versioning for the skill directory name — only bump it inside frontmatter

---

## Output Template

When applying this skill to manage a skill's lifecycle, produce:

1. **Version Classification Report** — Old version, change type (MAJOR/MINOR/PATCH), new version, and rationale referencing specific sections changed
2. **Backward Compatibility Results** — List of dependent skills checked, any trigger overlap issues found, and recommended actions
3. **Migration Guide** (if deprecating) — File path to the generated guide, affected skills list, timeline summary
4. **Cross-Reference Summary** — How many skills reference this one, which ones need updating, confirmation that all `related-skills` paths resolve
5. **Archive Manifest** (if retiring) — Source path, archive destination, sunset date, replacement skill name

---

## Related Skills

| Skill | Purpose |
|-------|---------|
| `skill-engineering` | Creating new skills from scratch — use before managing a new skill's lifecycle |
| `skill-router-system` | Understanding how the router index works — necessary context for deprecation and retirement steps |
| `skill-audit` | Assessing whether a skill has earned its place before deciding to deprecate or retire it |

## Live References

> Authoritative documentation links for this domain. The model follows markdown links at load time to resolve external references and inline content.

- [Semantic Versioning 2.0.0](https://semver.org/) — The semver specification for managing skill version numbers, deprecation signals, and compatibility
- [API Deprecation Best Practices (Microsoft)](https://learn.microsoft.com/en-us/azure/api-management/api-management-versioning) — Microsoft's guidance on API lifecycle management applicable to skill retirement workflows
- [Backward Compatibility Guidelines (Google)](https://cloud.google.com/apis/design/versioning) — Google Cloud's standards for maintaining backward compatibility during service evolution
- [Software Retirement and Migration Patterns (OWASP)](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/01-Information_Gathering/) — OWASP guidance on secure decommissioning of software components
- [Change Management Frameworks (ITIL 4)](https://www.axelos.com/certifications/itil-certifications/itil-4-foundation) — ITIL 4 standards for managing service lifecycle transitions including deprecation and retirement

