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.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add changkun/wallfacer --skill wf-spec-validate --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Wf Spec Validate?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/changkun-wf-spec-validate)More formats (shields.io, HTML) on the badges page.
---
name: wf-spec-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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!