Files
microsoft-SkillOpt/tests/test_plugin_sync.py
carpedkm 0be780052a feat: sync all 4 runtime plugins with full engine surface + fix #52 #58 #62
Bug fixes:
- #52: bundle run-sleep.sh in Claude Code plugin + 4-level fallback
- #58: add skillopt-sleep console script entry point in pyproject.toml
- #62: filter headless claude -p replay sessions from harvest

Plugin sync (Claude Code / Codex / Copilot / OpenClaw):
- Document all 22 CLI flags, 7 actions, 4 backends across all SKILL.md files
- Document config keys (preferences, gate_mode, dream_rollouts, etc.)
- Document memory consolidation (evolve_memory / evolve_skill)
- Add schedule/unschedule to all plugins
- Copilot MCP: expand schema from 3 → 16 params + schedule tools
- OpenClaw: add schedule/unschedule subcommands via shared scheduler

Tests:
- Cross-plugin parity test (prevents future feature drift)
- MCP schema completeness test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-20 11:31:09 +00:00

88 lines
3.2 KiB
Python

"""Cross-plugin parity tests — ensure all plugins document the same features.
Run: python3 -m pytest tests/test_plugin_sync.py -v
"""
import os
import unittest
REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
PLUGIN_SKILL_MDS = {
"claude-code": os.path.join(REPO, "plugins/claude-code/skills/skillopt-sleep/SKILL.md"),
"codex": os.path.join(REPO, "plugins/codex/skills/skillopt-sleep/SKILL.md"),
"openclaw": os.path.join(REPO, "plugins/openclaw/SKILL.md"),
}
MCP_SERVER = os.path.join(REPO, "plugins/copilot/mcp_server.py")
COPILOT_INSTRUCTIONS = os.path.join(REPO, "plugins/copilot/copilot-instructions.snippet.md")
CANONICAL_BACKENDS = {"mock", "claude", "codex", "copilot"}
def _read(path):
if not os.path.exists(path):
return ""
with open(path, encoding="utf-8") as f:
return f.read()
class TestPluginParity(unittest.TestCase):
def test_all_skill_mds_mention_all_backends(self):
for name, path in PLUGIN_SKILL_MDS.items():
text = _read(path)
if not text:
self.skipTest(f"{name} SKILL.md not found")
for backend in CANONICAL_BACKENDS:
self.assertIn(backend, text,
f"{name}/SKILL.md missing backend '{backend}'")
def test_all_skill_mds_mention_schedule(self):
for name, path in PLUGIN_SKILL_MDS.items():
text = _read(path)
if not text:
continue
self.assertIn("schedule", text.lower(),
f"{name}/SKILL.md missing 'schedule'")
self.assertIn("unschedule", text.lower(),
f"{name}/SKILL.md missing 'unschedule'")
def test_copilot_instructions_mention_schedule(self):
text = _read(COPILOT_INSTRUCTIONS)
self.assertIn("sleep_schedule", text)
self.assertIn("sleep_unschedule", text)
def test_copilot_instructions_mention_all_backends(self):
text = _read(COPILOT_INSTRUCTIONS)
for backend in CANONICAL_BACKENDS:
self.assertIn(backend, text,
f"copilot-instructions missing backend '{backend}'")
def test_mcp_server_has_schedule_tools(self):
text = _read(MCP_SERVER)
self.assertIn("sleep_schedule", text)
self.assertIn("sleep_unschedule", text)
def test_mcp_schema_has_key_params(self):
text = _read(MCP_SERVER)
for param in ["source", "tasks_file", "target_skill_path",
"max_sessions", "max_tasks", "auto_adopt", "json"]:
self.assertIn(f'"{param}"', text,
f"MCP schema missing param '{param}'")
def test_all_skill_mds_mention_memory_consolidation(self):
for name, path in PLUGIN_SKILL_MDS.items():
text = _read(path).lower()
if not text:
continue
has_mention = (
"memory consolidation" in text
or "evolve_memory" in text
or ("consolidate" in text and "memory" in text)
)
self.assertTrue(has_mention,
f"{name}/SKILL.md missing memory consolidation docs")
if __name__ == "__main__":
unittest.main()