validate
Validate content files for frontmatter completeness, accessibility, and structural correctness.
# Validate all content files
hwaro tool validate
# Validate a specific content directory
hwaro tool validate -c posts
# Fail CI on any warning, or cap the allowed warning count
hwaro tool validate --strict
hwaro tool validate --max-warnings 5
# Output as JSON
hwaro tool validate --json
Options
| Flag | Description |
|---|---|
| -c, --content-dir DIR | Content directory (default: content) |
| --strict | Treat warnings as errors when computing the exit code |
| --max-warnings N | Exit non-zero when warning count exceeds N (default: unlimited) |
| -j, --json | Output result as JSON |
| -h, --help | Show help |
What It Checks
- Missing
titlein frontmatter - Missing
descriptionin frontmatter - Images without alt text (
) - Broken internal links (
@/prefixed paths that don't resolve) - Frontmatter parse errors (TOML/YAML/JSON)
- Invalid date formats
- Mixed-case tags (e.g.,
Crystalinstead ofcrystal) - Draft files (reported as info)
Example Output
hwaro: validate content
content/blog/draft.md:
[warn] Missing description in frontmatter
[info] File is marked as draft
content/about.md:
[warn] Image missing alt text: 
[info] Tag has mixed case: "Crystal" (consider lowercase)
checked: 0 errors, 2 warnings, 2 info
In a color terminal the findings use ⚠/✗/ℹ glyphs under an hwaro validate
heading, and the closing line is a severity-colored ✦ checked outcome. The
command exits non-zero when error-level issues are found, so it can gate CI.
Exit codes mirror hwaro doctor: error-level findings exit with the content
error code (5), while warning-driven failures from --strict or
--max-warnings exit with the generic code (1) — a consumer can still tell a
broken file from a tightened gate. Both flags apply to --json runs too.
Rule IDs
| ID | Level | Description |
|---|---|---|
content-title-missing |
warning | Missing or "Untitled" title |
content-description-missing |
warning | Missing description |
content-alt-text-missing |
warning | Image without alt text |
content-internal-link-broken |
warning | Broken @/ internal link |
content-date-invalid |
warning | Unrecognized date format |
content-frontmatter-toml-error |
error | TOML frontmatter parse error |
content-frontmatter-yaml-error |
error | YAML frontmatter parse error |
content-frontmatter-json-error |
error | JSON frontmatter parse error |
content-read-error |
error | Failed to read content file |
content-tag-mixed-case |
info | Tag has mixed case |
content-draft |
info | File marked as draft |
JSON Output
{
"findings": [
{
"file": "content/blog/draft.md",
"line": null,
"rule": "content-description-missing",
"severity": "warning",
"message": "Missing description in frontmatter"
}
]
}