# Check Spec Structure

> Partial Skill: invoke by name only — project-spec/check-spec-structure's engine that audits a project spec's internal node-shape — feeds the formation Warden, not triggered by users directly.

- Skill: `cyberuni/check-spec-structure` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add cyberuni/check-spec-structure`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cyberuni/check-spec-structure/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cyberuni (https://skillmd.com/u/cyberuni)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cyberuni/check-spec-structure

---


# Check Spec Structure

The concrete engine for **spec-structure checking**.
It audits the **internal node-shape** of one project spec and returns a finding set for the
formation **Warden** — the **intra-spec** successor to the retired cross-spec `dedupe-specs`/`split-spec`
tools, now that one project is **one spec**. It carries a self-contained `.mts` script (the repo's
node-≥23.6 / no-deps convention). It is the **node-shape** sibling of `align-spec` (prose↔suite
alignment) and `concept-index` (the by-concept view).

## The three deterministic checks (and two judgment arms)

- **untagged-node** (blocking) — a spec-typed node README carrying no `concept:` tag, so it never
  appears in the by-concept index (`concept-index`). `--check` fails on it.
- **oversized-node** (advisory) — a node whose sibling `.feature` scenario count exceeds the
  granularity threshold. Carries a deterministic **shape profile** — plain scenario count, tagged
  (`@rubric`-preceded) scenario count, and section-cluster count (comment headers of either
  `# ── … ──` or `# ---- … ----` style) as a soft breadth hint — and prescribes **no route**.
  Surfaced in the audit but **never** fails `--check`.
- **incomplete-node** (advisory) — a **behavioral leaf spec** (a README with `spec-type: behavioral`
  and a colocated `.feature`) whose `spec.md` is missing one of the four required sections —
  `## What`, `## Use Cases`, `## Control Flow`, `## Scenario map` (`sdd:spec-format-governance`). A
  spec that stops at `## Use Cases` never draws its CFG or scenario map. Spec-type-aware: a reference
  artifact (`## Subject`, no scenario map) and an index node (no colocated `.feature`) are **not**
  held to the shape. **Advisory** while a corpus is brought up to the four-section shape; flip it to
  **blocking** with a follow-up once the corpus is clean.
- **breadth-vs-depth routing** and **intra-spec contradiction** are **Warden judgments** (the spec's
  `@rubric` scenarios) — the engine ships no code for them. The Warden reads the shape profile and
  routes: breadth (many clusters/plain scenarios) → propose a node split; depth with a deterministic
  suite → down-level via the verify-scenarios bridge; depth with agent-behavior scenarios → redesign.
  **placement-drift** is deliberately *not* a deterministic check: a concept legitimately scatters
  across folders (that scatter is what `concept-index` re-unifies), so a concept-vs-folder scan would
  false-positive on correctly-placed nodes.

## Run the scan

```bash
node "<skill>/scripts/check-spec-structure.mts" [--spec-dir <spec>] [--check] [--max-scenarios <n>] [--format toon|json]
```

- Default `--spec-dir` is the current directory; default `--format` is **TOON** (the token-efficient
  form the Warden scans); default `--max-scenarios` is **40**.
- **Audit mode** (default) emits the finding set — a `blocking[]` group then an `advisory[]` group,
  each finding naming the node. `--format json` emits the same findings as a flat JSON array.
- **`--check`** (CI guard) exits **non-zero** iff a **blocking** finding exists and **writes
  nothing**; advisory-only findings still exit zero. Wire it after `concept-index --check` in
  `verify:specs-new` so the project-spec stays structurally clean.

When `node` is absent, an agent performs the same derivation by hand: for each `<cap>/<unit>/README.md`
with a `spec-type`, flag it untagged if it carries no `concept:`; for each with a sibling `.feature`,
count `Scenario:` lines and flag oversized over the threshold.

## Boundaries

Frontmatter + scenario-count only — it never reads a node body, owns no lifecycle state, and writes
nothing. It **never acts** on a finding (a split, a reconcile) — that is the Warden's
(`sdd:formation-loop`) under its own self-clear-vs-escalate verdict. It does **not** render the
by-concept view (`concept-index`), advise a new node's home (`place-node`), or check gate legality
(`spec-gate`'s `check-spec-state`, fail-closed at the gate).

