spec-check
Validate a specification document's numbering and cross-references — duplicate or out-of-order headings and requirement IDs, orphan subsections, gaps, malformed identifiers, and dangling §/R reference
SKILL.mdclaude/plugins/specs/skills/spec-check/SKILL.md
GitHubSpec Check
Runs assets/spec_check.py over a specification. It reports every structural
defect it can decide from the text alone. It is the partner to spec-new: that
skill writes specs to a numbering discipline, and this one proves the discipline
held. Both ship in the luismc-specs plugin.
python3 "${CLAUDE_PLUGIN_ROOT}/skills/spec-check/assets/spec_check.py" specs/mine.md
Run it from the project root: the script path is absolute and the spec path is relative to the project. assets/spec_check.py sits alongside this file. It uses only the standard
library, so there is nothing to install.
Exit 0 means clean. Exit 1 means findings. Exit 2 means the file could not
be parsed. It takes any number of paths. A directory argument stands for the *.md files
under it, so a repo's whole specs/ directory can go in one run.
Citing another document
A bare §4.2 means this document's §4.2. A reference to another spec carries
its name: STACKSPEC §4.2. Register every document you cite. Otherwise its
references are skipped, not checked:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/spec-check/assets/spec_check.py" specs/mine.md \
--external STACKSPEC=<plugin>/skills/tech-stack-setup/references/invariants.md \
--external AUTHSPEC=<plugin>/skills/auth-setup/references/authentication.md
Point at the spec where it is maintained, inside the luismc-project-setup
plugin. Do not point at a copy inside a product. STACKSPEC §2.1 forbids a
product from vendoring either spec, so a product has no local path to register.
Its Stack Profile names the version instead, and the profile's numbering does not
match the spec's. If you register a Stack Profile here, STACKSPEC §5.3 resolves
against whatever the profile's own section 5.3 happens to be.
In this repository both specs are registered by default. You need the flag only for a document that cites something else.
Registration makes an outbound citation resolve against the other document.
Without it, STACKSPEC §9 falls through to internal resolution. It then matches
your own §9 if you have one. That is a silent false pass. It is the reason this
flag exists, rather than the checker guessing.
What it checks
Three families, all decidable from the document text. None needs judgement, which is why it can gate a build.
Headings — every number unique; heading depth matching the number's depth
(### 4.2 is level 3); no orphan subsection whose parent is missing; no gap in
a sibling run; ascending order.
Identifiers — every Rn.n defined once; the prefix matching the section it
sits in (an R7.3 inside §6 is a copy-paste that will resolve to the wrong
requirement); no gaps; ascending; nothing malformed like R6.6.1.
References — every §n.n resolves to a heading, and every Rn.n resolves to
a definition. The target may be in this document or in a registered one.
It deliberately does not flag two things. First, statutory citations
(§164.312, §1798.105). A flat floor of 100 excludes them, not "one past the
highest heading", because a tight ceiling silently reclassifies a dangling
reference as statutory. Second, a lettered insertion (R4.9a) is not a gap,
because that is how you add a requirement without renumbering.
The numbering rules it enforces
These come from the spec discipline spec-new writes to. They exist because
citations are the part of a spec that rots invisibly.
- Numbering is append-only. Never renumber to tidy up. A deletion leaves a gap, and the gap is the record that every citation stayed stable.
- Insert with a letter,
R4.9a, rather than shifting everything after it. §n.nandRn.nare different namespaces.§9is a heading;R9.1is a requirement. They look alike and people routinely confuse them. That is why the checker resolves them separately and reports them separately.
When it finds something
Fix the document, not the reference. A dangling §12.4 usually means a section
was deleted or renumbered. Repair it by restoring the number, or by pointing the
citation at what actually exists. Never renumber the target so the stale
citation happens to land, which just moves the break somewhere else.
Scope
It reads structure, not meaning. Two requirements that are each coherent but contradict each other pass here and need a human. Content checks tied to one particular document — entity tables, route maps, retention schedules — belong with that document, not in this skill.