# Validate

> 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.

- Skill: `latere-ai/validate` (Agent Skill)
- Install (CLI): `npx skillmds@latest add latere-ai/validate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/latere-ai/validate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: latere-ai (https://skillmd.com/u/latere-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/latere-ai/validate

---


# 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.

