Structural lint over the spec tree — required frontmatter fields, valid status and effort values, track location, DAG acyclicity, dispatch consistency, orphans, status consistency. Read-only; reports problems and fixes nothing. Use to check the tree is well-formed; use report for what the specs say about progress.
Installs into .claude/skills of the current project.
Are you the author of Validate?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/latere-ai-validate)
---
name: validate
description: Structural lint over the spec tree — required frontmatter fields, valid status and effort values, track location, DAG acyclicity, dispatch consistency, orphans, status consistency. Read-only; reports problems and fixes nothing. Use to check the tree is well-formed; use report for what the specs say about progress.
argument-hint: [spec-file.md]
allowed-tools: Read, Grep, Glob, Bash(ls *)
---
# Validate Specs
Run structural validation on spec documents against the repo's document model
— a document-model spec under `specs/`, an internals doc describing how specs
are parsed, or, failing both, the conventions below. If a specific spec file is given as
`$ARGUMENTS`, validate only that spec (and run cross-spec checks it
participates in). Otherwise, validate the entire spec tree.
## Step 0: Parse arguments
If an argument is provided, treat it as a single spec file to validate.
Otherwise, validate all specs under `specs/`.
## Step 1: Discover all specs
1. Glob for all spec files recursively: `specs/**/*.md` (excluding README.md
and any non-spec markdown files like changelogs).
2. For each spec file, parse YAML frontmatter between `---` fences. Extract:
`title`, `status`, `depends_on`, `affects`, `effort`, `created`,
`updated`, `author`, `dispatched_task_id`.
3. Determine leaf vs non-leaf: a spec is non-leaf if a subdirectory with the
same name (without `.md`) exists and contains at least one child spec.
4. Build the full spec tree (parent-child from filesystem) and the dependency
DAG (from `depends_on` edges).
## Step 2: Per-spec validation
For each spec, check these rules. Classify each finding as `error` or
`warning` per the severity column.
### Required fields (error)
`title`, `status`, `effort`, `created`, `updated`, `author` must all be present in
the frontmatter. Report each missing field. (`track` may be a frontmatter field or
derived from the path, depending on the repo's layout; see Valid track.)
### Valid status (error)
`status` must be one of: `vague`, `drafted`, `validated`, `testing`, `complete`,
`stale`, `archived` — the seven lifecycle states. `testing`
is the transient drift-verdict state between `validated` and `complete`; a spec in
`testing` is valid (not an error), though a long-lived `testing` carrying a
`testing_pending` reason is worth a warning. The server may also set the optional
fields `implementation_commit` (`base..tip`, present during `testing`) and
`testing_pending` (a reason string when the drift tester failed); both are valid.
### Valid track (error)
Grouping and ordering are independent axes, and they compose —
`specs/local/003-live-serve.md` is grouped by directory *and* numbered. Check
each separately; never infer one from the other, and never decide a single
layout for the whole repo from a majority vote.
**Grouping.** Every spec must have a track from exactly one source:
- A directory under `specs/` supplies it — the segment immediately after
`specs/`. A `track:` key that merely restates the directory is a removable
no-op.
- A spec sitting directly under `specs/` must carry a `track:` frontmatter
field. A spec with neither is an error.
**Ordering.** An `NNN-` filename prefix is an optional, *per-directory* reading
order. Judge it within each directory, never across the tree:
- A directory whose specs are numbered should number all of them; flag an
unnumbered straggler.
- Flag duplicate numbers inside one directory. The same number in two different
directories is fine — they are separate number spaces.
- A directory with no numbering at all is not an error. Do not ask for numbers
that were never adopted.
- An archived spec keeps the number it had (`specs/.archive/…/042-foo.md`), so
`depends_on` paths pointing at it still resolve.
Numbers are never the dependency order. That is `depends_on`, checked below.
### Valid effort (error)
`effort` must be one of: `small`, `medium`, `large`, `xlarge`.
### Date format (error)
`created` and `updated` must be valid ISO dates (YYYY-MM-DD). `updated` must
be greater than or equal to `created`.
### Dispatch consistency (error)
Non-leaf specs must have `dispatched_task_id: null` (or absent). Leaf specs
may have `null` or a valid UUID.
### `depends_on` targets exist (error)
Every path in `depends_on` must resolve to an existing spec file relative to
the repository root.
### No self-dependency (error)
A spec must not appear in its own `depends_on` list.
### `affects` paths exist (warning)
Every path in `affects` should resolve to an existing file or directory in the
codebase. Only a warning because code may not exist yet for `vague`/`drafted`
specs. Suppressed for `archived` specs — deleted paths are not actionable.
### Body not empty (warning)
Specs with status beyond `vague` should have meaningful content below the
frontmatter (more than just a title heading). Suppressed for `archived` specs —
a stub with only frontmatter is valid.
## Step 3: Cross-spec validation (tree-wide)
Run these checks across the full spec tree.
### DAG is acyclic (error)
Perform a topological sort on the `depends_on` graph. If a cycle is detected,
report the full cycle path (e.g., `A -> B -> C -> A`).
### No orphan directories (warning)
A `<name>/` subdirectory under a spec track should have a corresponding
`<name>.md` parent spec file in the same directory.
### No orphan specs (warning)
A `<name>.md` file that has a `<name>/` subdirectory should have at least one
child spec inside that directory.
### Status consistency (warning)
A `complete` non-leaf spec should not have incomplete leaves in its subtree.
Check recursively: if any leaf in the subtree has a status other than
`complete`, warn. Skipped when the non-leaf is `archived` — the subtree is
considered below glass regardless of leaf states.
### Stale propagation (warning)
If a spec is `stale`, check all specs that list it in their `depends_on`.
Those that are still `validated` should be flagged for review — their
assumptions about the stale spec may no longer hold. Does not fire for
`archived` dependencies — a validated spec depending on an archived spec
receives a `dependency-is-archived` advisory note instead (see below).
### Track location (warning)
Where a spec sits in a track directory, warn if it also carries a `track:` key —
the directory already supplies it and the loader ignores the field. Where a spec
sits directly under `specs/`, there is no directory to cross-check, so this
warning does not apply; a missing `track:` there is the error above.
### dependency-is-archived (warning)
A live spec whose `depends_on` includes an archived spec. Advisory only —
recommend removing the edge or documenting why it still matters. Does not
count as a stale-propagation warning.
### Unique dispatches (error)
No two specs may share the same non-null `dispatched_task_id` value. Collect
all `dispatched_task_id` values and report duplicates.
## Step 4: Generate report
Present findings grouped by severity, then by spec:
```
## Spec Validation Report
Specs scanned: N
Errors: N
Warnings: N
### Errors
#### specs/shared/sandbox-backends.md
- [error] Missing required field: author
- [error] depends_on target does not exist: specs/shared/nonexistent.md
#### specs/local/foo.md
- [error] Invalid status: "wip" (must be vague|drafted|validated|testing|complete|stale|archived)
### Cross-Spec Errors
- [error] Cycle detected: A.md -> B.md -> C.md -> A.md
- [error] Duplicate dispatched_task_id "abc-123": specs/a.md, specs/b.md
### Warnings
#### specs/cloud/bar.md
- [warning] affects path does not exist: internal/cloud/bar.go
- [warning] Body is empty for a "drafted" spec
### Cross-Spec Warnings
- [warning] Orphan directory: specs/shared/old-feature/ has no parent spec
- [warning] Stale propagation: specs/shared/api.md is stale, but
specs/local/client.md (validated) depends on it
### Verdict: PASS / N errors, M warnings
```
If there are zero errors, the verdict is **PASS**. If there are errors, list the
count. Warnings alone do not cause a failure.
## Notes
- This skill is **read-only**. It does not modify any files.
- When validating a single spec (`$ARGUMENTS` provided), still run cross-spec
checks that involve that spec (its `depends_on` targets, specs that depend on
it, cycle detection through it).
- Specs without YAML frontmatter are reported as having all required fields
missing — they may be legacy specs that predate the document model.
- When the repo implements its own spec validator, these rules mirror it.
Report a disagreement rather than silently preferring one side.