Design: Shared Frontmatter Module + ruamel.yaml Migration
Date: 2026-02-19 Status: Approved
Problem Statement
Two related issues:
False quoting convention — 30 files across 7 categories instruct agents to quote ALL YAML frontmatter descriptions. YAML only requires quotes when values contain syntax-breaking characters (
:followed by space,#preceded by space, leading special chars). Agents propagate this as "project convention," producing unnecessarily quoted descriptions across ~45 files.Wrong YAML library — 9 scripts use
pyyaml(import yaml). The project needsruamel.yaml(preserves comments, round-trips formatting). Additionally,tomlkitis used by scripts but missing from pyproject.toml dev dependencies.Duplicated frontmatter code —
plugin_validator.pyandvalidate_frontmatter.pyboth implementextract_frontmatter()+ Pydantic validation + auto-fix logic independently.task_format.pyhas a third parser. No shared module exists.CI anti-pattern —
.github/workflows/code-quality.ymlcontains inline Python for YAML/TOML syntax validation, duplicating pre-commit hookscheck-yamlandcheck-tomlalready configured in.pre-commit-config.yaml.
Verified Facts
All claims verified by context-gathering agents with file:line evidence.
Frontmatter Tooling Landscape
plugin_validator.py(4147 lines, created 2026-02-02, 22 commits) — superset validator with FM001-NR002 error codes, Pydantic models, token complexity. Pre-commit hook runs this.validate_frontmatter.py(1310 lines, created 2026-01-25, 13 commits) — original validator with 12+ unique functions not in plugin_validator.py:validate_skill_directory_name(),generate_plugin_metadata(),validate_plugin_registration(), Rich CLI.task_format.py— library module withparse_yaml_frontmatter(),update_yaml_field()(surgical regex-based field updates).python-frontmatter>=1.1.0— already in pyproject.toml. Used by.claude/utilities/find-temp-documentation.py. Supports custom handlers viaBaseHandlersubclass.
pyyaml Reference Categories (68 references, 27 files)
- 18 IMPORT — 9 scripts with active
import yamlusage - 9 DEPENDENCY — PEP 723 metadata and pyproject.toml entries
- 26 EXAMPLE — docs showing pyyaml as a library (hatchling, bandit, box.md)
- 2 INSTRUCTION — agent docs recommending/referencing pyyaml
- 1 DETECTION — warning message checking pyyaml installation
- 5 HISTORICAL — planning docs
- 7 REFERENCE — architecture/usage docs
Quoting Instruction Scope (30 files, 7 categories)
- 9 active instruction files agents read and follow
- 5 validator scripts with enforcement code (FM004, FM009)
- Error code docs (ERROR_CODES.md x2, ARCHITECTURE.md, USAGE.md)
- CLAUDE.md / README files (4 files)
- Planning/architecture docs (4 files)
- Test files (2 files)
- Audit/analysis files (2 files)
CI Duplication
.github/workflows/code-quality.ymllines ~430-479: inlineimport tomllibanduv run --with pyyaml python -c "import yaml; yaml.safe_load(...)"for syntax checking.pre-commit-config.yamlline 39:check-yamlwithargs: [--unsafe].pre-commit-config.yamlline 42:check-toml- Pre-commit already runs in CI via
prek
Sibling Repo Patterns
mkapidocs— centralizedyaml_utils.pymodule with ruamel.yaml for round-trip operations (validated pattern)mcp-json-yaml-toml— uses bothruamel.yaml>=0.18.0andtomlkit>=0.14.0python-frontmatter— supports custom handlers; no built-in ruamel.yaml support; requiresBaseHandlersubclass withload()andexport()overrides
fix_tool_formats.py
Confirmed: pyyaml>=6.0.0 in PEP 723 deps but never imported. Pure regex script. Dead dependency.
Design
1. Shared Frontmatter Module
File: plugins/plugin-creator/scripts/frontmatter_utils.py
Layer 1 — RuamelYAMLHandler: python-frontmatter BaseHandler subclass using ruamel.yaml with preserve_quotes=True. Implements load() and export(). ~30 lines.
Layer 2 — Convenience API:
load_frontmatter(path: Path) -> frontmatter.Post— reads markdown file, returns Post with parsed metadata and content bodyloads_frontmatter(text: str) -> frontmatter.Post— from stringdump_frontmatter(post: frontmatter.Post) -> str— serializes to markdown string with frontmatterdumps_frontmatter(post: frontmatter.Post, path: Path) -> None— writes to fileupdate_field(path: Path, field: str, value: Any) -> None— surgical single-field update
PEP 723 deps: ruamel.yaml>=0.18.0, python-frontmatter>=1.1.0
2. Script Migrations (9 files)
Each script replaces custom frontmatter extraction + import yaml with imports from the shared module:
plugin_validator.py— replaceextract_frontmatter()+yaml.safe_load()+yaml.dump()validate_frontmatter.py— same patternquick_validate.py— replaceimport yaml+yaml.safe_load()task_format.py— replaceimport yaml+yaml.safe_load()+ custom_format_yaml_value()migrate_task_format.py— replaceimport yamlsplit_task_file.py— replace pyyaml dep + usagesync_gitlab_docs.py— replaceimport yaml+yaml.safe_load()discover_linters.py— replace lazyimport yamlfallback with ruamel.yaml (reads.pre-commit-config.yaml, not frontmatter)fix_tool_formats.py— remove unused pyyaml PEP 723 dep only
3. Validator Error Code Changes
- FM009 (Unquoted description with colons) — Keep. Changes purpose: detects descriptions that would break YAML parsing from unquoted colons. Auto-fix still adds quotes. Instruction files change from "always quote" to "avoid colons; if necessary, quote the value. ruamel.yaml handles quoting on write, but broken YAML from hand-editing won't parse at all."
- FM004 (Forbidden multiline indicator) — Keep unchanged. Claude Code skill indexer cannot parse
>-,|-,|— platform limitation. - FM007/FM008 (multiline description normalization) — Keep unchanged.
4. CI Inline Validation Removal
Delete inline Python blocks in .github/workflows/code-quality.yml (lines ~430-479). Pre-commit hooks check-yaml and check-toml already provide this coverage.
5. Instruction File Updates (30 files)
- Active instruction files (9): Change "always quote descriptions" to "avoid colons in descriptions; quote only when YAML syntax requires it"
- Validator scripts (5): Handled in Section 2 and 3
- Error code docs (4): Update FM009 description to match new purpose
- CLAUDE.md / README files (4): Update auto-fix capability descriptions
- Planning/architecture docs (4): Update if actively referenced by agents
- Test files (2): Update fixtures and assertions for new FM009 behavior
- Audit/analysis files (2): Update if touched
6. Dependency Changes
pyproject.toml:
- Add
"ruamel.yaml>=0.18.0"to dev dependencies - Add
"tomlkit>=0.13.0"to dev dependencies - Remove
"types-pyyaml>=6.0.12.20250915" - Keep
"python-frontmatter>=1.1.0"(already present)
PEP 723 across migrated scripts:
"pyyaml>=6.0"->"ruamel.yaml>=0.18.0"- Remove
"types-pyyaml>=6.0" fix_tool_formats.py: remove"pyyaml>=6.0.0"entirelydiscover_linters.py: remove"types-pyyaml>=6.0.0", keep"tomlkit>=0.13.0"
7. Documentation Example Updates
Project convention docs (update pyyaml -> ruamel.yaml):
.claude/docs/TASK_FILE_FORMAT.mdplugins/plugin-creator/references/ARCHITECTURE.mdplugins/plugin-creator/references/USAGE.mdplugins/plugin-creator/skills/add-doc-updater/references/doc-updater-template.mdplugins/python3-development/agents/code-reviewer.mdplugins/python3-development/agents/python-cli-design-spec.md
Third-party library reference docs (update to ruamel.yaml equivalents):
plugins/holistic-linting/.../bandit/deserialization.mdplugins/python3-development/.../modern-modules/box.mdplugins/python3-development/.../modern-modules/shiv.mdplugins/python3-development/.../hatchling/filesplugins/python3-development/.../planning/reference-document-architecture.md
tomllib/tomli references: Leave stdlib references in modernpython and stdlib-scripting skills unchanged — they teach stdlib capabilities accurately.
CLAUDE.md convention statement: Add to project CLAUDE.md that this repo uses ruamel.yaml and tomlkit — never pyyaml.
8. Mechanical Quote Normalization
Run the shared module across all frontmatter-bearing component files (skills, agents, commands, rules) — using the same file type detection as plugin_validator.py's FileType enum. ruamel.yaml round-trip mode normalizes quoting automatically on read-write cycle.
This is the last step, run after all instruction files and validators are updated.
Execution Order
flowchart TD
S1[1. Shared frontmatter module] --> S2[2. Script migrations]
S6[6. Dependency changes] --> S1
S6 --> S2
S2 --> S3[3. Validator error code changes]
S3 --> S5[5. Instruction file updates]
S4[4. CI inline removal] -.-> |parallel with S2| S2
S7[7. Doc example updates] -.-> |parallel with S5| S5
S5 --> S8[8. Mechanical quote normalization]
- First: S6 (dependency changes — unblocks everything)
- Second: S1 (shared module — unblocks migrations)
- Parallel group: S2 (script migrations) + S4 (CI cleanup)
- After S2: S3 (validator changes)
- Parallel group: S5 (instruction updates) + S7 (doc updates)
- Last: S8 (mechanical normalization — after all rules are updated)
Verification
uv run prek run --all-files— all formatting/linting passes- All migrated scripts execute without import errors
plugin_validator.pyvalidates a known-good skill file successfullyplugin_validator.pycorrectly flags a description with unquoted colons (FM009)plugin_validator.pycorrectly flags multiline indicators (FM004)- Existing tests in
test_frontmatter_validator.pypass (with updated assertions for FM009) - The mechanical normalization script processes all component files without errors
- Spot-check: 5 frontmatter files have correct quoting (quoted where YAML requires, unquoted otherwise)
- No Python file in
plugins/or.github/containsimport yaml(as an actual import, not as detection guidance or documentation)