.claude-plugin/skills/config-filename-validation/SKILL.md
# Skill: Config Filename/ID Validation Pattern ## Overview | Field | Value | |-------|-------| | Date | 2026-02-19 | | Issue | #733 (follow-up to #692) | | PR | #795 | | Objective | Add validation that tier config filenames match their `tier` field | | Outcome | Success — 2213 tests pass, 73.38% coverage | | Category | testing | ## When to Use Apply this pattern when: - A config dataclass has an ID/key field that should match its filename - You want to catch silent mismatches between filena
npx skillsauth add homericintelligence/projectscylla .claude-plugin/skills/config-filename-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 | #733 (follow-up to #692) |
| PR | #795 |
| Objective | Add validation that tier config filenames match their tier field |
| Outcome | Success — 2213 tests pass, 73.38% coverage |
| Category | testing |
Apply this pattern when:
Trigger conditions:
scylla/config/validation.pyFor simple exact-match validation (no normalization needed):
def validate_filename_tier_consistency(config_path: Path, tier: str) -> list[str]:
"""Validate that config filename matches the tier field."""
warnings = []
filename_stem = config_path.stem
# Skip validation for test fixtures (prefixed with _)
if filename_stem.startswith("_"):
return warnings
# Check exact match
if filename_stem == tier:
return warnings
# Mismatch detected
warnings.append(
f"Config filename '{filename_stem}.yaml' does not match tier "
f"'{tier}'. Expected '{tier}.yaml'"
)
return warnings
For IDs requiring normalization (e.g., : → - for model IDs), see
validate_filename_model_id_consistency — adds a get_expected_filename() helper
and checks both exact and normalized match.
In scylla/config/loader.py, update the relevant load_X() method:
from .validation import validate_filename_model_id_consistency, validate_filename_tier_consistency
# After constructing the config object:
try:
config = TierConfig(**data)
except Exception as e:
raise ConfigurationError(f"Invalid tier configuration in {tier_path}: {e}")
# Validate filename/tier consistency
warnings = validate_filename_tier_consistency(tier_path, config.tier)
for warning in warnings:
logger.warning(warning)
return config
Key decisions:
config.tier not data["tier"] — field may have been normalized by validatorsTestFilenameTierConsistencyclass TestFilenameTierConsistency:
def test_filename_matches_tier_exact(self, tmp_path, caplog):
# exact match → no warnings
def test_filename_mismatch_warns(self, tmp_path, caplog):
# mismatch → 1 warning containing filename, tier field, expected filename
def test_test_fixtures_skip_validation(self, tmp_path):
# _-prefixed filename → import validation fn directly, no loader call needed
from scylla.config.validation import validate_filename_tier_consistency
config_path = tmp_path / "_test-fixture.yaml"
warnings = validate_filename_tier_consistency(config_path, "t0")
assert not warnings
def test_warning_message_format(self, tmp_path, caplog):
# assert exact message text contains filename and tier field
_-prefixed tier nameAttempted: loader.load_tier("_test-fixture") — mirroring how model fixture test uses load_model("_test-fixture")
Why it failed: load_tier() normalizes the tier name:
tier = tier.lower().strip()
if not tier.startswith("t"):
tier = f"t{tier}" # "_test-fixture" → "t_test-fixture"
So it would look for t_test-fixture.yaml (not found) rather than _test-fixture.yaml.
Fix: Test the validation function directly instead of going through the loader. Import validate_filename_tier_consistency and call it with a manually constructed path.
Ruff reformatted tests/unit/test_config_loader.py on first commit attempt. The pre-commit hook modifies files in place but the commit still fails. Re-stage and commit again — the second commit succeeds.
| File | Change |
|------|--------|
| scylla/config/validation.py | Added validate_filename_tier_consistency() (+31 lines) |
| scylla/config/loader.py | Import + 7 lines in load_tier() |
| tests/unit/test_config_loader.py | Added TestFilenameTierConsistency class (+84 lines) |
2213 passed, 8 warnings
Coverage: 73.38% (threshold: 73%)
This exact pattern (validation function + loader call + 4 tests) can be applied to any new config type with a filename-matching field. The only variation needed:
:), add a normalization helper_-prefix fixture casedevelopment
# 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