Skills › Compound Engineering › ce-compound › validate-frontmatter.py
validate-frontmatter.py
137 lines · skills/ce-compound/scripts/validate-frontmatter.py at 3449230
1#!/usr/bin/env python3
2"""Validate ce-compound learning-doc frontmatter for parser-safety issues.
4Usage:
5 python3 validate-frontmatter.py <doc-path>
7Exit codes:
8 0 — frontmatter passes all checks
9 1 — validation failure (diagnostics on stderr)
10 2 — usage error (bad arguments, missing file)
12Scope: this script catches *parser-safety* issues — frontmatter that strict
13YAML parsers will silently misread. It does NOT validate against the
14schema's required-field or enum-value rules; that's a separate concern. The
15intent is to prevent the silent-data-loss bug class where YAML's quoting
16rules truncate or reframe scalar values without raising.
18Checks (regex-based, no YAML parser dependency):
19 1. File starts and ends frontmatter with `---` lines (matched as full
20 lines, not substrings — `----` and `---extra` are rejected)
21 2. No top-level scalar value contains ` #` unquoted (silent comment
22 truncation — what Codex caught on PR #695)
23 3. No top-level scalar value contains `: ` unquoted (mapping confusion —
24 what surfaced in a 2026-04-16 plan doc's `title:` field)
26The script does NOT flag values starting with YAML reserved indicators
27(`` ` ``, `*`, `&`, `!`, etc.) because those produce loud parser errors
28downstream rather than silent corruption — they're already caught by
29whatever consumes the doc. This validator's purpose is silent-corruption
30prevention, not lint.
32Pure-stdlib (no PyYAML or other third-party deps). Runs in <50ms typical.
33Designed to produce concrete, actionable error messages so the calling
34agent can fix and retry without ambiguity.
35"""
36import os
37import re
38import sys
41def usage_fail(msg: str) -> "NoReturn":
42 sys.stderr.write(f"validate-frontmatter: {msg}\n")
43 sys.exit(2)
46def main(argv: list[str]) -> int:
47 if len(argv) != 2:
48 usage_fail(f"usage: {os.path.basename(argv[0])} <doc-path>")
50 doc_path = argv[1]
51 if not os.path.isfile(doc_path):
52 usage_fail(f"file not found: {doc_path}")
54 with open(doc_path) as f:
55 text = f.read()
57 issues: list[str] = []
59 # Check 1: frontmatter delimiters. Match the delimiter as a complete
60 # line whose stripped content is exactly `---` — substring matching
61 # (e.g. `text.find("\n---", 4)`) would falsely accept `----` or
62 # `---extra` as a terminator and let malformed docs slip through to
63 # downstream parsers that require a strict `---` line.
64 lines = text.split("\n")
65 if not lines or lines[0].rstrip() != "---":
66 sys.stderr.write(
67 f"FAIL: {doc_path}\n"
68 f" file does not start with '---' frontmatter delimiter line\n"
69 )
70 return 1
72 end_idx: int | None = None
73 for i in range(1, len(lines)):
74 if lines[i].rstrip() == "---":
75 end_idx = i
76 break
78 if end_idx is None:
79 sys.stderr.write(
80 f"FAIL: {doc_path}\n"
81 f" frontmatter not closed (no '---' line after the opening delimiter)\n"
82 )
83 return 1
85 fm_text = "\n".join(lines[1:end_idx])
87 # Checks 2 & 3: silent-corruption quoting risks on top-level scalar
88 # fields. We scan line-by-line and only flag top-level mapping entries
89 # (no leading whitespace) whose value isn't already quoted/structured.
90 for lineno, line in enumerate(fm_text.split("\n"), start=2):
91 stripped = line.lstrip()
92 if not stripped or stripped.startswith("#"):
93 continue
94 if ":" not in line:
95 continue
96 # Top-level mapping keys only — skip nested values, array items
97 if line.startswith((" ", "\t")):
98 continue
99 # Skip pure list-marker lines like "- item" (these can't be top-level
100 # in our frontmatter convention, but be defensive)
101 if stripped.startswith("- "):
102 continue
104 key, _, val = line.partition(":")
105 val_stripped = val.strip()
106 if not val_stripped:
107 # Key with no value on this line — likely a parent of a nested
108 # block (`tags:` followed by `- foo`). Nothing to validate here.
109 continue
110 # Already quoted or structured (block scalar, flow collection)
111 if val_stripped[0] in '"\'[{|>':
112 continue
114 if re.search(r"\s#", val_stripped):
115 issues.append(
116 f"line {lineno}: '{key.strip()}' value contains ' #' — quote it. "
117 "YAML treats space-then-# as a comment delimiter and silently "
118 "drops the rest of the value."
119 )
120 if re.search(r":\s", val_stripped):
121 issues.append(
122 f"line {lineno}: '{key.strip()}' value contains ': ' — quote it. "
123 "Strict YAML parsers may treat this as a nested mapping."
124 )
126 if issues:
127 sys.stderr.write(f"FAIL: {doc_path}\n")
128 for issue in issues:
129 sys.stderr.write(f" {issue}\n")
130 return 1
132 print(f"OK: {doc_path}")
133 return 0
136if __name__ == "__main__":
137 sys.exit(main(sys.argv))