.claude-plugin/skills/model-config-naming-validation/SKILL.md
# Skill: model-config-naming-validation ## Overview | Field | Value | |------------|----------------------------------------------------| | Date | 2026-02-19 | | Issue | #682 | | PR | #769 | | Objective | CI check that filename matches model_id in YAML configs | | Outcome | Success — 28 tests
npx skillsauth add homericintelligence/projectscylla .claude-plugin/skills/model-config-naming-validationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Field | Value | |------------|----------------------------------------------------| | Date | 2026-02-19 | | Issue | #682 | | PR | #769 | | Objective | CI check that filename matches model_id in YAML configs | | Outcome | Success — 28 tests, all pre-commit hooks pass |
scripts/scripts/ in tests (mypy module conflict resolution)# scripts/validate_model_configs.py
def find_model_configs(config_dir: Path) -> list[Path]:
# Skip test fixtures prefixed with '_'
return sorted(f for f in config_dir.glob("*.yaml") if not f.name.startswith("_"))
def check_filename_consistency(config: dict, file_path: Path) -> list[str]:
model_id = config.get("model_id")
stem = file_path.stem
# Allow version suffixes: stem must be a prefix of model_id
if model_id != stem and not model_id.startswith(stem + "-"):
return [f" Filename mismatch: stem='{stem}', model_id='{model_id}'"]
return []
Key design decisions:
_-prefixed files (test fixtures) — avoids false positives from fixtures with toy valuesstem + "-") so claude-sonnet-4-5.yaml with model_id: "claude-sonnet-4-5-20250929" passesyaml.safe_load for parsing (requires pyyaml in pixi dependencies)scripts/__init__.py (CRITICAL for mypy)When a test imports from scripts.validate_model_configs, mypy will error:
scripts/validate_model_configs.py: error: Source file found twice under different module names:
"validate_model_configs" and "scripts.validate_model_configs"
Fix: touch scripts/__init__.py — makes scripts/ a proper package so mypy resolves the module name consistently. This is required whenever any test file contains from scripts.<module> import ....
tests/unit/scripts/Create tests/unit/scripts/__init__.py and tests/unit/scripts/test_<name>.py.
Use tmp_path pytest fixture for isolated file system operations:
def write_yaml(directory: Path, filename: str, content: str) -> Path:
path = directory / filename
path.write_text(textwrap.dedent(content))
return path
Parametrize consistency checks across multiple valid/invalid cases:
@pytest.mark.parametrize("filename,model_id", [
("claude-opus-4-1.yaml", "claude-opus-4-1"), # exact match
("claude-sonnet-4-5.yaml", "claude-sonnet-4-5-20250929"), # version suffix
])
def test_consistent_configs_pass(self, filename, model_id, tmp_path):
...
# .pre-commit-config.yaml
- id: validate-model-configs
name: Validate Model Config Naming
description: Ensure model config filenames match the model_id and required fields are present
entry: python scripts/validate_model_configs.py config/models/
language: system
files: ^config/models/.*\.yaml$
pass_filenames: false
Important: pass_filenames: false — the script takes the directory, not individual files. Without this, pre-commit would pass each changed YAML as an argument, breaking the script.
Pre-commit hooks auto-format with ruff on first commit attempt; files are modified. Re-stage the reformatted files and commit again — second attempt passes cleanly.
--namespace-packages mypy flagTried mypy --namespace-packages to resolve the "found twice" error without adding __init__.py. The flag did not resolve the issue. Root cause: mypy sees the file both as a top-level source (via scripts/ path argument) and as scripts.validate_model_configs (via the test import).
Fix: touch scripts/__init__.py — the only reliable solution.
pytest tests/unit/scripts/This passes all 28 tests but reports 0% coverage and fails the fail-under=73 threshold. Always run pytest tests/ (full suite) to verify coverage. The new script tests don't add to the scylla/ coverage source configured in pyproject.toml.
| Metric | Value |
|-----------------|-------------------------------------|
| Tests added | 28 |
| Tests total | 2233 |
| Coverage | 73.35% (threshold: 73%) |
| Pre-commit hook | validate-model-configs |
| Hook trigger | ^config/models/.*\.yaml$ |
| Fixtures skipped| Files prefixed with _ |
#!/usr/bin/env python3
"""One-line description.
Usage:
python scripts/validate_<thing>.py [dir]
"""
import sys
from pathlib import Path
import yaml
REQUIRED_FIELDS = ["field1", "field2"]
def find_configs(config_dir: Path) -> list[Path]:
return sorted(f for f in config_dir.glob("*.yaml") if not f.name.startswith("_"))
def validate_config(file_path: Path) -> list[str]:
try:
config = yaml.safe_load(file_path.read_text())
except yaml.YAMLError as e:
return [f" YAML parse error: {e}"]
if not isinstance(config, dict):
return [f" Expected mapping, got {type(config).__name__}"]
return [f" Missing: '{f}'" for f in REQUIRED_FIELDS if f not in config]
def main() -> int:
config_dir = Path(sys.argv[1] if len(sys.argv) > 1 else "config/models")
configs = find_configs(config_dir)
failed = [f for f in configs if validate_config(f)]
for f in failed:
print(f"FAIL: {f}")
for e in validate_config(f):
print(e)
print(f"\n{len(configs) - len(failed)}/{len(configs)} passed.")
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())
development
# Skill: docs-status-fix ## Overview | Field | Value | |------------|----------------------------------------------------| | Date | 2026-02-19 | | Category | documentation | | Objective | Fix stale "Current Status" in CLAUDE.md | | Issue | #753 | | PR | #810
tools
# Skill: preflight-closing-issues-fix ## Overview | Field | Value | |-------|-------| | Date | 2026-02-21 | | Issue | #802 | | PR | #912 | | Category | tooling | | Objective | Fix `preflight_check.sh` Check 3 false positives caused by free-text PR search matching issue numbers in unrelated PR titles/bodies | | Outcome | Success — 6 bash tests pass, all pre-commit hooks green, PR created with auto-merge | ## When to Use Trigger this skill when: - A preflight/guard script uses `gh pr list --s
tools
# Preflight Check Skill Propagation ## Overview | Field | Value | |-------|-------| | Date | 2026-02-21 | | Issue | #803 | | Objective | Add preflight check to `worktree-create` skill so developers bypassing `gh-implement-issue` still run the 6-check safety gate | | Outcome | Success — PR #917 created, auto-merge enabled | | Files Changed | `tests/claude-code/shared/skills/worktree/worktree-create/SKILL.md` | ## When to Use Use this pattern when: - A safety/quality gate exists in one entry-
tools
# Orphan Config Detection ## Overview | Field | Value | |------------|-----------------------------------------------------------------| | Date | 2026-02-20 | | Issue | #777 | | PR | #824 | | Objective | Warn when a `config/models/*.yaml` file