Files
BMAD-METHOD/tools/skill-validator.md
Alex Verkhovsky 49069b8b52 feat(quick-dev): render templates via stdlib Python at skill entry (#2281)
* feat(quick-dev): render templates via stdlib Python at skill entry

Move compile-time variable substitution out of the LLM and into a
deterministic Python step. SKILL.md becomes a two-line stdout-dispatch
shim that runs render.py and follows the instruction it prints. The
renderer reads BMad configuration from the central four-layer TOML
surface introduced in #2285 (_bmad/config.toml plus config.user.toml
and the two _bmad/custom/ overrides), with a fallback to the legacy
per-module _bmad/bmm/config.yaml for pre-#2285 installs.

Compile-time refs ({{.var}}) get substituted at render time. LLM-runtime
refs ({var}) pass through untouched.

Renderer (render.py)
- Python 3 stdlib only (tomllib, already bundled since 3.11). UTF-8 I/O.
  Every invocation rebuilds from scratch — no hash, no cache.
- find_project_root walks up from cwd; HALT to stdout if no _bmad/
  is found anywhere on the path.
- load_central_config deep-merges the four TOML layers in priority
  order (base-team → base-user → custom-team → custom-user) so user
  overrides in _bmad/custom/config.user.toml win over installer-
  regenerated base values. flatten_central_config lifts scalar keys
  from [core] and [modules.bmm] into the renderer's flat namespace;
  module keys beat core on collision (matches the installer's own
  core-key-stripping behavior).
- When _bmad/config.toml is absent, falls through to the legacy
  flat-YAML parser for _bmad/bmm/config.yaml — the renderer keeps
  working across the #2285 transition.
- {{.var}} substitution; unresolved refs emit empty string (Go
  missingkey=zero semantics).
- Smart defaults for planning_artifacts / implementation_artifacts /
  communication_language applied after config load. Derives
  sprint_status / deferred_work_file from implementation_artifacts.
  {{.main_config}} points at whichever surface was actually read.
- Renders every .md in the skill dir except SKILL.md to
  {project-root}/_bmad/render/bmad-quick-dev/.
- On success, stderr summary plus a single stdout line:
  "read and follow {workflow_md}". On failure, stdout HALT directive —
  per the Anthropic skills spec, script stdout is the defined agent-
  communication channel.

Skill entry (SKILL.md)
- Two-line shim: run python render.py, follow stdout. No template
  tokens in SKILL.md itself.

Template conversions
- workflow.md, step-01..05, step-oneshot, sync-sprint-status: convert
  every compile-time {var} reference to {{.var}}. Runtime refs
  preserved.
- spec-template.md untouched (single-curly comment hint stays as
  documentation).

Skill-prose cleanups bundled in
- Remove dead step-file frontmatter: empty-string variable declarations
  (spec_file, story_key, diff_output, review_mode) in quick-dev step-01
  and code-review step-01; empty --- --- blocks in step-03 and step-05;
  the specLoopIteration counter init moved from step-04 frontmatter into
  the step body where first-entry vs loopback semantics are explicit.
- Unify the language rule across all six quick-dev step files plus
  workflow.md.

Tooling
- tools/validate-skills.js: add TPL-01 rule. Files whose name contains
  "template" must not contain compile-time {{.var}} substitutions.
  Template files seed durable, version-controlled artifacts that
  execute on other machines; baking a value at render time would
  freeze a machine-local path into every downstream artifact.
- tools/validate-file-refs.js: add render/ to INSTALL_ONLY_PATHS so
  the validator recognizes the runtime-generated buffer.
- tools/skill-validator.md: document TPL-01; deterministic rule count
  bumped from 14 to 15.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(quick-dev): drop render.py YAML fallback and smart defaults

Single happy path: central _bmad/config.toml with four-layer merge,
Python 3.11+ required (no ImportError guard), HALT if config missing.
Deletes load_flat_yaml, the YAML fallback branch, the setdefault block
for planning_artifacts/implementation_artifacts/communication_language,
and the tomllib ImportError fallback.

Part of plan-quick-dev-python-config-hardening.md (F0).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): normalize render.py paths to forward slashes

On Windows, os.path.join returns backslash-separated paths that can
misrender as escape sequences when later concatenated into POSIX
shell strings or regexes. Normalize the project root to forward
slashes after find_project_root, and use posixpath.join for every
path that gets baked into rendered .md files or joined into config
values. os.makedirs and os.listdir accept forward-slash paths on
Windows, so their call sites stay as-is.

Part of plan-quick-dev-python-config-hardening.md (F3).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): preserve source line endings in render.py

Python text-mode open() with the platform default performs universal-
newline translation: on Windows, LF source files get written as CRLF,
producing spurious diffs when rendered output is compared against
source. Pass newline="" on both the source read and the rendered
write so line endings pass through verbatim.

Part of plan-quick-dev-python-config-hardening.md (F4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): delete stale .md renders before rebuilding

render.py rebuilds from scratch per the docstring, but
makedirs(exist_ok=True) only overwrites files that still exist in
the source — stale outputs from renamed/deleted source files linger
in _bmad/render/bmad-quick-dev/ forever. Remove every .md in the
render dir before the render loop; keep the dir itself and any
non-.md files.

Part of plan-quick-dev-python-config-hardening.md (F5).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): scope render/ whitelist to bmad-quick-dev

The previous INSTALL_ONLY_PATHS entry 'render/' was a blanket prefix
that let every {project-root}/_bmad/render/... reference in any skill
slip past validation. Narrow to 'render/bmad-quick-dev/' so only this
skill's render buffer is whitelisted. Future skills adopting the
stdout-dispatch renderer pattern add their own entries explicitly.

Part of plan-quick-dev-python-config-hardening.md (F6).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(quick-dev): add renderer smoke test with TOML override

New test/test-quick-dev-renderer.js spins up a temp project with
base _bmad/config.toml and a _bmad/custom/config.user.toml override,
runs render.py, and asserts the override wins in rendered workflow.md
and that sprint_status is rooted at an absolute path in the temp
project. Registered as test:renderer in package.json and chained
into the npm test script.

Part of plan-quick-dev-python-config-hardening.md (F7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): HALT cleanly when base config.toml is unparseable

Load the four config layers through a load_toml helper that marks the
base _bmad/config.toml as required. A missing, unparseable, or unreadable
base now prints a HALT directive to stdout and exits, instead of being
silently skipped and then crashing downstream with a KeyError when a
derived value (e.g. implementation_artifacts) is absent. Optional layers
still warn on stderr and fall back to empty. Merge semantics are
unchanged (dict-aware deep merge, override wins for lists and scalars).

* fix(quick-dev): resolve render.py via {skill-root} in skill entry shim

The bare `python render.py` shim assumes the agent's working directory is
the skill directory, but agents run from the project root, so the script
is not found. Reference it as `{skill-root}/render.py` — BMAD's standard
token for a skill's installed directory, already used by every other
skill's resolve_customization.py invocation — and add the one-line
`{skill-root}` explainer so the model resolves it from an instruction
rather than guessing. Interpreter stays `python`; the python vs python3
choice is a separate cross-platform concern.

* refactor(quick-dev): resolve [workflow] customization in render.py

render.py now merges the three customize layers (customize.toml ->
custom/bmad-quick-dev.toml -> .user.toml) with the same structural rules as
resolve_customization.py and inlines the resolved [workflow] values, so no
{workflow.*} placeholder survives. workflow.md drops its Step 1 runtime
resolver + manual-merge fallback; step-05 and step-oneshot drop their runtime
workflow.on_complete calls. The shared resolve_customization.py and every
other skill are untouched. Smoke test extended with a [workflow] override
fixture covering inlining, array append, and no-leak assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): harden render.py invocation in the SKILL.md shim

The shim called bare `python`, which can resolve to Python 2 or be absent;
render.py needs 3.11+ for tomllib. Spell out python3 and the version
requirement. Also make the exit code authoritative: on a non-zero exit
(including an uncaught crash that writes only to stderr), do not proceed --
report what was printed and stop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(quick-dev): drop the render.py success stderr line

The "rendered N files" progress line was pure diagnostic noise. The shim
already tells the LLM to ignore stderr and follow the stdout instruction, so
on success render.py now prints only the "read and follow ..." line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(quick-dev): drop the activation gate sentence from the rendered workflow

The gate ported from #2398 defended against runtime customization
indirection: agents guessed resolver outputs instead of executing them,
silently skipping append steps. render.py inlines the prepend/append
entries into the rendered workflow.md, so there is nothing left to
short-circuit, and each inlined list already carries its own execute-
in-order imperative. In the default install both lists render as
_None._ and the gate is pure noise.

* feat(quick-dev): materialize review layers into invocation blocks

Reconcile #2550 with render-time [workflow] resolution. Main made review
layers configurable as [[workflow.review_layers]] arrays of tables and
had the LLM resolve them during activation; this branch resolves the
[workflow] block in render.py instead, so activation-time resolution no
longer exists and the layer refs must be materialized at render time.

Rather than inlining the layer tables as data plus interpretation rules,
render.py now knows this skill's customization schema outright and
renders review_layers/oneshot_review_layers as direct invocation blocks:
disabled layers (empty instruction) drop out, each active layer becomes
a #### section holding its instruction verbatim, zero active layers
renders the HALT instruction, and runtime placeholders like
{diff_output} pass through. The only judgment left to the LLM is the
optional `when` condition, which renders as a run-time guard line.
The step-04/step-oneshot review intros collapse to a single execute-in-
parallel imperative. Smoke test covers default rendering, replace-by-id,
disable-by-empty-instruction, when-guards, and the all-disabled HALT.

* fix(quick-dev): invoke render.py via uv run per house standard

The SKILL.md shim launched render.py with bare `python3`, which the rest
of BMAD is migrating away from: the customize-bmad docs and the
installer's uv-check standardize on `uv run` (uv provisions a suitable
3.11+ interpreter on demand). Bare `python3` is also fragile on Windows,
where python.org installs expose `python`/`py` rather than `python3`.

Make `uv run` the primary invocation and demote `python3` to the
documented fallback, spelling out `python`/`py -3` for Windows and the
3.11+ tomllib requirement. render.py itself is unchanged; the renderer
test drives it directly and is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(quick-dev): HALT cleanly on missing or malformed config

render.py derived sprint_status/deferred_work_file from an unconditional
vars_["implementation_artifacts"] subscript, so a config lacking that key
raised a raw KeyError instead of the stdout HALT the rest of the script
uses on bad input. flatten_central_config likewise called .get("bmm") on
merged["modules"] without checking it was a table, so a non-table
[modules] crashed with an AttributeError.

Guard both: HALT with a clear stdout directive when implementation_artifacts
is missing or blank, and coerce a non-dict modules to {} before indexing.
Add renderer regression tests asserting each path exits without a Python
traceback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(quick-dev): resolve config at compile time, drop the runtime re-read

The activation "Load Config" step told the LLM to open {{.main_config}}
and re-resolve project_name, communication_language, sprint_status, etc.
at run time -- but render.py already bakes those from the full four-layer
config merge. main_config pointed at only the base _bmad/config.toml, so
on installs with override layers (config.user.toml / custom/*) the runtime
re-read saw stale values that could contradict the baked {{.var}} in the
same rendered file. It also handed resolution back to the LLM: the drift
this skill's renderer exists to remove.

Delete the ceremony and wire each value where it is actually used:

- Every value the step resolved is already inlined at its point of use
  (planning/implementation_artifacts, sprint_status, communication_language)
  or loaded via persistent_facts (project-context.md), so the central
  block was pure redundancy.
- Fold document_output_language into the per-step language rule, adopting
  the house-canonical form ("Speak in X. Write any file output in Y.")
  already used by bmad-checkpoint-preview.
- Move the {date} = current-datetime definition to step-02, where the
  spec template's {date} field is filled.
- Drop the user greeting (user_name) and user_skill_level tailoring:
  quick-dev is not a conversational skill and neither was load-bearing.
- Remove main_config from render.py; it had no remaining consumer.

Renderer tests repointed at the files that now carry these values, plus
coverage for document_output_language baking and main_config removal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(quick-dev): reference variables by bare name, not placeholder curlies

Curlies mean "expand this to the value"; a bare backticked name means
"this is the variable/field I'm talking about". Several step files wrapped
a variable in curlies where they were only naming, assigning, passing, or
testing it -- so the notation implied an expansion that never happens:

- step-01: identify `epic_num`/`story_num`, set/leave `story_key` unset
- step-02: test `preserved_intent`; and resolve the template's `date` field
  (was `{date}`, which read as "expand date here" rather than naming it)
- step-03/step-05/step-oneshot: pass `target_status` to sync-sprint-status,
  set `title`
- sync-sprint-status: the `target_status` parameter, `story_key` precondition,
  and both `target_status` conditionals

Value tokens that are genuinely materialized in place -- `{spec_file}` paths,
`development_status[{story_key}]`, "set ... to `{target_status}`" -- stay
curly. Also reword step-02's frozen-block instruction from the ambiguous
"substitute it for the `<frozen-after-approval>` block" to "replace the
`<frozen-after-approval>` block in the spec you just filled out with
`preserved_intent`" so it's clear the replacement happens in the artifact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(quick-dev): require uv, drop the python3 interpreter fallback

The SKILL.md shim tried `uv run render.py` and, if uv was missing, retried
with a bare `python3`/`py -3` interpreter. Nothing else in the codebase
does that interpreter fallback: the uv-based skills (bmad-prd, bmad-ux,
bmad-architecture, bmad-product-brief) fall back to reading customize.toml
and using defaults -- graceful feature degradation, never a different
runner -- and the legacy skills just call python3 outright. uv is the
established house runner (memlog.py, resolve_customization.py, lint_spine.py
all invoke it).

That graceful-degrade path does not exist here: render.py is the entry
dispatch that produces the workflow.md the LLM then follows, so there is
nothing to fall back to. The only honest outcomes are "uv runs it" or
"HALT". Make uv the floor and drop the fallback.

Pin the interpreter the house way -- a PEP 723 `requires-python = ">=3.11"`
block, matching memlog.py/lint_spine.py -- so `uv run` provisions a 3.11+
interpreter and the tomllib requirement is guaranteed rather than hoped for.
This replaces the prose "needs 3.11+" hedge the shim used to carry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-07 19:31:55 -07:00

23 KiB

Skill Validator — Inference-Based

An LLM-readable validation prompt for skills following the Agent Skills open standard.

First Pass — Deterministic Checks

Before running inference-based validation, run the deterministic validator:

node tools/validate-skills.js --json path/to/skill-dir

This checks 13 rules deterministically: SKILL-01, SKILL-02, SKILL-03, SKILL-04, SKILL-05, SKILL-06, SKILL-07, PATH-02, STEP-01, STEP-06, STEP-07, SEQ-02, TPL-01.

Review its JSON output. For any rule that produced zero findings in the first pass, skip it during inference-based validation below — it has already been verified. If a rule produced any findings, the inference validator should still review that rule (some rules like SKILL-04 and SKILL-06 have sub-checks that benefit from judgment). Focus your inference effort on the remaining rules that require judgment (PATH-01, PATH-03, PATH-04, PATH-05, WF-03, STEP-02, STEP-03, STEP-04, STEP-05, SEQ-01, REF-01, REF-02, REF-03).

How to Use

  1. You are given a skill directory path to validate.
  2. Run the deterministic first pass (see above) and note which rules passed.
  3. Read every file in the skill directory recursively.
  4. Apply every rule in the catalog below to every applicable file, skipping rules that passed the deterministic first pass.
  5. Produce a findings report using the report template at the end, including any deterministic findings from the first pass.

If no findings are generated (from either pass), the skill passes validation.


Definitions

  • Skill directory: the folder containing SKILL.md and all supporting files.
  • Internal reference: a file path from one file in the skill to another file in the same skill.
  • External reference: a file path from a skill file to a file outside the skill directory.
  • Originating file: the file that contains the reference (path resolution is relative to this file's location).
  • Config variable: a name-value pair whose value comes from the project config file (e.g., planning_artifacts, implementation_artifacts, communication_language).
  • Runtime variable: a name-value pair whose value is set during workflow execution (e.g., spec_file, date, status).
  • Intra-skill path variable: a frontmatter variable whose value is a path to another file within the same skill — this is an anti-pattern.

Rule Catalog

SKILL-01 — SKILL.md Must Exist

  • Severity: CRITICAL
  • Applies to: skill directory
  • Rule: The skill directory must contain a file named SKILL.md (exact case).
  • Detection: Check for the file's existence.
  • Fix: Create SKILL.md as the skill entrypoint.

SKILL-02 — SKILL.md Must Have name in Frontmatter

  • Severity: CRITICAL
  • Applies to: SKILL.md
  • Rule: The YAML frontmatter must contain a name field.
  • Detection: Parse the --- delimited frontmatter block and check for name:.
  • Fix: Add name: <skill-name> to the frontmatter.

SKILL-03 — SKILL.md Must Have description in Frontmatter

  • Severity: CRITICAL
  • Applies to: SKILL.md
  • Rule: The YAML frontmatter must contain a description field.
  • Detection: Parse the --- delimited frontmatter block and check for description:.
  • Fix: Add description: '<what it does and when to use it>' to the frontmatter.

SKILL-04 — name Format

  • Severity: HIGH
  • Applies to: SKILL.md
  • Rule: The name value must start with bmad-, use only lowercase letters, numbers, and single hyphens between segments.
  • Detection: Regex test: ^bmad-[a-z0-9]+(-[a-z0-9]+)*$.
  • Fix: Rename to comply with the format (e.g., bmad-my-skill).

SKILL-05 — name Must Match Directory Name

  • Severity: HIGH
  • Applies to: SKILL.md
  • Rule: The name value in SKILL.md frontmatter must exactly match the skill directory name. The directory name is the canonical identifier used by installers, manifests, and skill: references throughout the project.
  • Detection: Compare the name: frontmatter value against the basename of the skill directory (i.e., the immediate parent directory of SKILL.md).
  • Fix: Change the name: value to match the directory name, or rename the directory to match — prefer changing name: unless other references depend on the current value.

SKILL-06 — description Quality

  • Severity: MEDIUM
  • Applies to: SKILL.md
  • Rule: The description must state both what the skill does AND when to use it. Max 1024 characters.
  • Detection: Check length. Look for trigger phrases like "Use when" or "Use if" — their absence suggests the description only says what but not when.
  • Fix: Append a "Use when..." clause to the description.

SKILL-07 — SKILL.md Must Have Body Content

  • Severity: HIGH
  • Applies to: SKILL.md
  • Rule: SKILL.md must have non-empty markdown body content after the frontmatter. The body provides L2 instructions — a SKILL.md with only frontmatter is incomplete.
  • Detection: Extract content after the closing --- frontmatter delimiter and check it is non-empty after trimming whitespace.
  • Fix: Add markdown body with skill instructions after the closing ---.

WF-03 — workflow.md Frontmatter Variables Must Be Config or Runtime Only

  • Severity: HIGH

  • Applies to: workflow.md frontmatter

  • Rule: Every variable defined in workflow.md frontmatter must be either:

    • A config variable (value references {project-root} or a config-derived variable like {planning_artifacts})
    • A runtime variable (value is empty, a placeholder, or set during execution)
    • A legitimate external path expression (must not violate PATH-05 — no paths into another skill's directory)

    It must NOT be a path to a file within the skill directory (see PATH-04), nor a path into another skill's directory (see PATH-05).

  • Detection: For each frontmatter variable, check if its value resolves to a file inside the skill (e.g., starts with ./, {installed_path}, or is a bare relative path to a sibling file). If so, it is an intra-skill path variable. Also check if the value is a path into another skill's directory — if so, it violates PATH-05 and is not a legitimate external path.

  • Fix: Remove the variable. Use a hardcoded relative path inline where the file is referenced.


PATH-01 — Internal References Must Be Relative From Originating File

  • Severity: CRITICAL
  • Applies to: all files in the skill
  • Rule: Any reference from one file in the skill to another file in the same skill must be a relative path resolved from the directory of the originating file. Use ./ prefix for siblings or children, ../ for parent traversal. Bare relative filenames in markdown links (e.g., [text](sibling.md)) are also acceptable.
  • Detection: Scan for file path references (in markdown links, frontmatter values, inline backtick paths, and prose instructions like "Read fully and follow"). Verify each internal reference uses relative notation (./, ../, or bare filename). Always resolve the path from the originating file's directory — a reference to ./steps/step-01.md from a file already inside steps/ would resolve to steps/steps/step-01.md, which is wrong.
  • Examples:
    • CORRECT: ./steps/step-01-init.md (from workflow.md at skill root to a step)
    • CORRECT: ./template.md (from workflow.md to a sibling)
    • CORRECT: ../template.md (from steps/step-01.md to a skill-root file)
    • CORRECT: workflow.md (bare relative filename for sibling)
    • CORRECT: ./step-02-plan.md (from steps/step-01.md to a sibling step)
    • WRONG: ./steps/step-02-plan.md (from a file already inside steps/ — resolves to steps/steps/)
    • WRONG: {installed_path}/template.md
    • WRONG: {project-root}/.claude/skills/my-skill/template.md
    • WRONG: /Users/someone/.claude/skills/my-skill/steps/step-01.md
    • WRONG: ~/.claude/skills/my-skill/file.md

PATH-02 — No installed_path Variable

  • Severity: HIGH
  • Applies to: all files in the skill
  • Rule: The installed_path variable is an anti-pattern from the pre-skill workflow era. It must not be defined in any frontmatter, and {installed_path} must not appear anywhere in any file.
  • Detection: Search all files for:
    • Frontmatter key installed_path:
    • String {installed_path} anywhere in content
    • Markdown/prose assigning installed_path (e.g., `installed_path` = `.`)
  • Fix: Remove all installed_path definitions. Replace every {installed_path}/path with ./path (relative from the file that contains the reference). If the reference is in a step file and points to a skill-root file, use ../path instead.

PATH-03 — External References Must Use {project-root} or Config Variables

  • Severity: HIGH
  • Applies to: all files in the skill
  • Rule: References to files outside the skill directory must use {project-root}/... or a config-derived variable path (e.g., {planning_artifacts}/..., {implementation_artifacts}/...).
  • Detection: Identify file references that point outside the skill. Verify they start with {project-root} or a known config variable. Flag absolute paths, home-relative paths (~/), or bare paths that resolve outside the skill.
  • Fix: Replace with {project-root}/... or the appropriate config variable.

PATH-05 — No File Path References Into Another Skill

  • Severity: HIGH
  • Applies to: all files in the skill
  • Rule: A skill must never reference any file inside another skill's directory by file path. Skill directories are encapsulated — their internal files (steps, templates, checklists, data files, workflow.md) are private implementation details. The only valid way to reference another skill is via skill:skill-name syntax, which invokes the skill as a unit. Reaching into another skill to cherry-pick an internal file (e.g., a template, a step, or even its workflow.md) breaks encapsulation and creates fragile coupling that breaks when the target skill is moved or reorganized.
  • Detection: For each external file reference (frontmatter values, markdown links, inline paths), check whether the resolved path points into a directory that is or contains a skill (has a SKILL.md). Patterns to flag:
    • {project-root}/_bmad/.../other-skill/anything.md
    • {project-root}/_bmad/.../other-skill/steps/...
    • {project-root}/_bmad/.../other-skill/templates/...
    • References to old pre-conversion locations that were skill directories (e.g., core/workflows/skill-name/ when the skill has since moved to core/skills/skill-name/)
  • Fix:
    • If the intent is to invoke the other skill: replace with skill:skill-name.
    • If the intent is to use a shared resource (template, data file): the resource should be extracted to a shared location outside both skills (e.g., core/data/, bmm/data/, or a config-referenced path) — not reached into from across skill boundaries.

PATH-04 — No Intra-Skill Path Variables

  • Severity: MEDIUM
  • Applies to: all files (frontmatter AND body content)
  • Rule: Variables must not store paths to files within the same skill. These paths should be hardcoded as relative paths inline where used. This applies to YAML frontmatter variables AND markdown body variable assignments (e.g., `template` = `./template.md` under a ### Paths section).
  • Detection: For each variable with a path-like value — whether defined in frontmatter or in body text — determine if the target is inside the skill directory. Indicators: value starts with ./, ../, {installed_path}, or is a bare filename of a file that exists in the skill. Exclude variables whose values are prefixed with a config variable like {planning_artifacts}, {implementation_artifacts}, {project-root}, or other config-derived paths — these are external references and are legitimate.
  • Fix: Remove the variable. Replace each {variable_name} usage with the direct relative path.
  • Exception: If a path variable is used in 4+ locations across multiple files and the path is non-trivial, a variable MAY be acceptable. Flag it as LOW instead and note the exception.

STEP-01 — Step File Naming

  • Severity: MEDIUM
  • Applies to: files in steps/ directory
  • Rule: Step files must be named step-NN-description.md where NN is a zero-padded two-digit number. An optional single-letter variant suffix is allowed for branching steps (e.g., step-01b-continue.md).
  • Detection: Regex: ^step-\d{2}[a-z]?-[a-z0-9-]+\.md$
  • Fix: Rename to match the pattern.

STEP-02 — Step Must Have a Goal Section

  • Severity: HIGH
  • Applies to: step files
  • Rule: Each step must clearly state its goal. Look for a heading like ## YOUR TASK, ## STEP GOAL, ## INSTRUCTIONS, ## INITIALIZATION, ## EXECUTION, # Step N:, or a frontmatter goal: field.
  • Detection: Scan for goal-indicating headings (including # Step N: Title as a top-level heading that names the step's purpose) or frontmatter.
  • Fix: Add a clear goal section.

STEP-03 — Step Must Reference Next Step

  • Severity: MEDIUM
  • Applies to: step files (except the final step)
  • Rule: Each non-terminal step must contain a reference to the next step file for sequential execution.
  • Detection: Look for ## NEXT section or inline reference to a next step file. Remember to resolve the reference from the originating file's directory (PATH-01 applies here too).
  • Fix: Add a ## NEXT section with the relative path to the next step.
  • Note: A terminal step is one that has no next-step reference and either contains completion/finalization language or is the highest-numbered step. If a workflow branches, there may be multiple terminal steps.

STEP-04 — Halt Before Menu

  • Severity: HIGH
  • Applies to: step files
  • Rule: Any step that presents a user menu (e.g., [C] Continue, [A] Approve, [S] Split) must explicitly HALT and wait for user response before proceeding.
  • Detection: Find menu patterns (bracketed letter options). Check that text within the same section (under the same heading) includes "HALT", "wait", "stop", "FORBIDDEN to proceed", or equivalent.
  • Fix: Add an explicit HALT instruction before or after the menu.

STEP-05 — No Forward Loading

  • Severity: HIGH
  • Applies to: step files
  • Rule: A step must not load or read future step files until the current step is complete. Just-in-time loading only.
  • Detection: Look for instructions to read multiple step files simultaneously, or unconditional references to step files with higher numbers than the current step. Exempt locations: ## NEXT sections, navigation/dispatch sections that list valid resumption targets, and conditional routing branches.
  • Fix: Remove premature step loading. Ensure only the current step is active.

STEP-06 — Step File Frontmatter: No name or description

  • Severity: MEDIUM
  • Applies to: step files
  • Rule: Step files should not have name: or description: in their YAML frontmatter. These are metadata noise — the step's purpose is conveyed by its goal section and filename.
  • Detection: Parse step file frontmatter for name: or description: keys.
  • Fix: Remove name: and description: from step file frontmatter.

STEP-07 — Step Count

  • Severity: LOW
  • Applies to: workflow as a whole
  • Rule: A sharded workflow should have between 2 and 10 step files. More than 10 risks LLM context degradation.
  • Detection: Count files matching step-*.md in the steps/ directory.
  • Fix: Consider consolidating steps if over 10.

SEQ-01 — No Skip Instructions

  • Severity: HIGH
  • Applies to: all files
  • Rule: No file should instruct the agent to skip steps or optimize step order. Sequential execution is mandatory.
  • Detection: Scan for phrases like "skip to step", "jump to step", "skip ahead", "optimize the order", "you may skip". Exclude negation context (e.g., "do NOT skip steps", "NEVER skip") — these are enforcement instructions, not skip instructions.
  • Exception: Conditional routing (e.g., "if X, go to step N; otherwise step M") is valid workflow branching, not skipping.

SEQ-02 — No Time Estimates

  • Severity: LOW
  • Applies to: all files
  • Rule: Workflow files should not include time estimates. AI execution speed varies too much for estimates to be meaningful.
  • Detection: Scan for patterns like "takes X minutes", "~N min", "estimated time", "ETA".
  • Fix: Remove time estimates.

TPL-01 — Template Files Must Not Contain Compile-Time Substitutions

  • Severity: HIGH
  • Applies to: .md files whose name contains template (case-insensitive)
  • Rule: Template files seed durable, version-controlled artifacts (e.g. spec files) that execute on other machines. A {{.var}} compile-time substitution would be baked at render time and freeze a machine-local value into every artifact produced from the template.
  • Detection: Regex \{\{\.\w+\}\} match anywhere in a file whose basename matches /template/i.
  • Fix: Remove the {{.var}} reference. Use single-curly {var} if the value should be resolved at LLM runtime by the consumer of the generated artifact.

REF-01 — Variable References Must Be Defined

  • Severity: HIGH
  • Applies to: all files
  • Rule: Every {variable_name} reference in any file (body text, frontmatter values, inline instructions) must resolve to a defined source. Valid sources are:
    1. A frontmatter variable in the same file
    2. A frontmatter variable in the skill's workflow.md (workflow-level variables are available to all steps)
    3. A known config variable from the project config (e.g., project-root, planning_artifacts, implementation_artifacts, communication_language)
    4. A known runtime variable set during execution (e.g., date, status, project_name, user-provided input variables)
  • Detection: Collect all {...} tokens in the file. For each, check whether it is defined in the file's own frontmatter, in workflow.md frontmatter, or is a recognized config/runtime variable. Flag any token that cannot be traced to a source. Use the config variable list from the project's config.yaml as the reference for recognized config variables. Runtime variables are those explicitly described as user-provided or set during execution in the workflow instructions.
  • Exceptions:
    • Double-curly {{variable}} — these are template placeholders intended to survive into generated output (e.g., {{project_name}} in a template file). Do not flag these.
    • Variables inside fenced code blocks that are clearly illustrative examples.
  • Fix: Either define the variable in the appropriate frontmatter, or replace the reference with a literal value. If the variable is a config variable that was misspelled, correct the spelling.

REF-02 — File References Must Resolve

  • Severity: HIGH
  • Applies to: all files
  • Rule: All file path references within the skill (markdown links, backtick paths, frontmatter values) should point to files that plausibly exist.
  • Detection: For internal references, verify the target file exists in the skill directory. For external references using config variables, verify the path structure is plausible (you cannot resolve config variables, but you can check that the path after the variable looks reasonable — e.g., {planning_artifacts}/*.md is plausible, {planning_artifacts}/../../etc/passwd is not).
  • Fix: Correct the path or remove the dead reference.

REF-03 — Skill Invocation Must Use "Invoke" Language

  • Severity: HIGH
  • Applies to: all files
  • Rule: When a skill references another skill by name, the surrounding instruction must use the word "invoke". The canonical form is Invoke the \skill-name` skill`. Phrases like "Read fully and follow", "Execute", "Run", "Load", "Open", or "Follow" are invalid — they imply file-level operations on a document, not skill invocation. A skill is a unit that is invoked, not a file that is read.
  • Detection: Find all references to other skills by name (typically backtick-quoted skill names like `bmad-foo`). Check the surrounding instruction text (same sentence or directive) for file-oriented verbs: "read", "follow", "load", "execute", "run", "open". Flag any that do not use "invoke" (or a close synonym like "activate" or "launch").
  • Fix: Replace the instruction with Invoke the \skill-name` skill. Remove any "read fully and follow" or similar file-oriented phrasing. Do NOT add a skill:` prefix to the name — use natural language.

Report Template

When reporting findings, use this format:

# Skill Validation Report: {skill-name}

**Directory:** {path}
**Date:** {date}
**Files scanned:** {count}

## Summary

| Severity | Count |
| -------- | ----- |
| CRITICAL | N     |
| HIGH     | N     |
| MEDIUM   | N     |
| LOW      | N     |

## Findings

### {RULE-ID} — {Rule Title}

- **Severity:** {severity}
- **File:** `{relative-path-within-skill}`
- **Line:** {line number or range, if identifiable}
- **Detail:** {what was found}
- **Fix:** {specific fix for this instance}

---

(repeat for each finding, grouped by rule ID)

## Passed Rules

(list rule IDs that produced no findings)

If zero findings: report "All {N} rules passed. No findings." and list all passed rule IDs.


Skill Spec Cheatsheet

Quick-reference for the Agent Skills open standard. For the full standard, see: Agent Skills specification

Structure

  • Every skill is a directory with SKILL.md as the required entrypoint
  • YAML frontmatter between --- markers provides metadata; markdown body provides instructions
  • Supporting files (scripts, templates, references) live alongside SKILL.md

Path resolution

  • Relative file references resolve from the directory of the file that contains the reference, not from the skill root
  • Example: from branch-a/deep/next.md, ./deeper/final.md resolves to branch-a/deep/deeper/final.md
  • Example: from branch-a/deep/next.md, ./branch-b/alt/leaf.md incorrectly resolves to branch-a/deep/branch-b/alt/leaf.md

Frontmatter fields (standard)

  • name: lowercase letters, numbers, hyphens only; max 64 chars; no "anthropic" or "claude"
  • description: required, max 1024 chars; should state what the skill does AND when to use it

Progressive disclosure — three loading levels

  • L1 Metadata (~100 tokens): name + description loaded at startup into system prompt
  • L2 Instructions (<5k tokens): SKILL.md body loaded only when skill is triggered
  • L3 Resources (unlimited): additional files + scripts loaded/executed on demand; script output enters context, script code does not

Key design principle

  • Skills are filesystem-based directories, not API payloads — Claude reads them via bash/file tools
  • Keep SKILL.md focused; offload detailed reference to separate files

Practical tips

  • Keep SKILL.md under 500 lines
  • description drives auto-discovery — use keywords users would naturally say