协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
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.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: skill-lifecycle-management description: 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. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: agent triggers: skill lifecycle, versioning strategy, skill deprecation, backward compatibility, skill retirement, migration guide, how do i sunset a skill archetypes:
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.
metadata.version in frontmatter to match the classified bumprelated-skillspython3 scripts/generate_readme.pyUse this skill when:
Avoid this skill for:
skill-engineering instead)skill-audit instead)skill-router-system instead)┌──────────────┐ ┌───────────────┐ ┌───────────────┐
│ Idea │ │ Design │ │ Create │
│ & Scope │───▶│ Spec + │───▶│ SKILL.md │
│ │ │ Schema │ │ │
└──────────────┘ └───────────────┘ └───────┬───────┘
│
┌──────────────┐ ┌───────────────┐ ┌─────────▼─────────┐
│ Retire │◀───│ Deprecate │◀───│ Deploy & │
│ (archive) │ │ (mark + │ │ Validate │
│ │ │ migrate) │ │ │
└──────────────┘ └───────────────┘ └─────────┬─────────┘
│
┌──────────────▼─────────┐
│ Monitor & │
│ Version Bump │
└────────────────────────┘
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.
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.
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.
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.
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.
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.
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.
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 |
When a skill should no longer be actively developed but must remain available for existing auto-routing:
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
Before publishing a version change, verify that no dependent skills break. This function scans the entire skills directory for cross-reference integrity.
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}")
When a skill reaches end-of-life, follow this archive procedure:
# 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:
Sunset timeline:
/skill.archive/, remove from skills-index.json active list# ❌ 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
<!-- ❌ 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 -->
# ❌ 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
skills-index.json and regenerate README.md after every version change/skill <name>related-skills fields after retirementWhen applying this skill to manage a skill's lifecycle, produce:
related-skills paths resolve| 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 |
Authoritative documentation links for this domain. The model follows markdown links at load time to resolve external references and inline content.