Skip to content

CI Overview + Validator Catalog

This document maps every GitHub Actions workflow + every validator script in pm-skills + how they fit together. Use it when:

  • You’re a contributor wanting to understand what CI will check before you open a PR
  • You’re a maintainer auditing why a check fired (or didn’t fire)
  • You’re authoring a new validator and want to know where to wire it
  • You’re triaging a CI failure and want to know which workflow file owns which check

pm-skills CI has three layers:

  1. Local pre-commit / pre-push (developer machine; optional). Run via individual script invocation or the scripts/pre-tag-validate.{sh,ps1} orchestration bundle.
  2. GitHub Actions on PR (automatic; every push to a non-default branch + every PR open/update). The 8 workflow files in .github/workflows/.
  3. GitHub Actions on main + tag (automatic on push to main + tag push). Same workflow definitions; some have additional triggers like release: created for the doc-deploy + release-zips paths.

Most validators that fail in layer 2 would have caught the issue in layer 1 if run locally. Maintainers are encouraged to run pre-tag-validate before every release-prep commit.

flowchart TD
    PushPR[Push to non-main branch<br/>or PR open/update] --> Validation[validation.yml<br/>ubuntu + windows]
    PushPR --> Plugin[validate-plugin.yml]
    PushPR --> CodeQL[codeql.yml]

    PushMain[Push to main<br/>docs/ path filter] --> Pages[deploy-pages.yml]
    PushMain --> Drafts[create-issues-from-drafts.yml]

    TagPush[Tag push v*.*.*] --> Release[release.yml]
    TagPush --> Zips[release-zips.yml]

    Manual[workflow_dispatch] --> SyncAgents[sync-agents-md.yml]

    Validation --> Enforcing[enforcing validators:<br/>lint-skills-frontmatter<br/>validate-agents-md<br/>validate-commands<br/>family validators<br/>validate-docs-frontmatter --strict<br/>check-no-body-h1 --strict<br/>check-count-consistency<br/>check-skill-cross-references<br/>gen + astro build<br/>check-rendered-links + anchors<br/>check-route-parity<br/>+ more]
    Validation --> Advisory[Advisory validators:<br/>check-frontmatter-yaml<br/>check-version-references]

    Enforcing -->|all pass| Green[PR green; merge unblocked]
    Enforcing -->|any fail| Red[PR red; fix + push again]
    Advisory -.->|informational| Green

    Pages --> Site[Astro 6.3.x build<br/>publish to GitHub Pages]
    Release --> GH[Publish GitHub Release<br/>auto notes + manual edit]
    Zips --> Artifacts[Attach zips to Release]

    Local[Local pre-commit:<br/>pre-tag-validate.sh / .ps1] -.->|runs the validator scripts| Enforcing

The diagram shows the trigger -> workflow -> validator -> outcome flow. The dotted arrow from pre-tag-validate to the enforcing validators indicates the local-machine equivalent: running the bundle locally exercises the same validator scripts the CI runner runs. The bundle does NOT run the Astro build (deploy-pages.yml), plugin-install validation (validate-plugin.yml), edit-link verification, or cross-doc reference checks, so a green local bundle is a strong - but not complete - predictor of a green PR check.

All 8 workflow files in .github/workflows/:

WorkflowTriggerWhat It Does
validation.ymlPush to any branch + PR open/updateRuns the full enforcing validator suite on ubuntu-latest + windows-latest. This is the primary CI gate.
validate-plugin.ymlPush + PR + workflow_dispatchValidates the plugin manifest at .claude-plugin/plugin.json + the marketplace manifest at .claude-plugin/marketplace.json + commands + skill structure.
sync-agents-md.ymlWorkflow_dispatch onlyManual maintainer trigger to regenerate AGENTS.md from skill directory state. Hardened with two-layer defense (apply:true input gate + contents:read token gate).
codeql.ymlPush + PR + scheduleCodeQL static analysis for security + code quality.
create-issues-from-drafts.ymlPush to main (issue-draft path filter) + workflow_dispatchAuto-creates GitHub issues from docs/internal/issue-drafts/ when they merge to main.
release.ymlTag push (v*.*.* pattern) + workflow_dispatchPublishes the GitHub Release with auto-generated notes (maintainer edits the body post-tag for full notes).
release-zips.ymlTag push (v*.*.* pattern)Builds the release zip artifacts and attaches them to the GitHub Release.
deploy-pages.ymlPush to main (docs path filter) + workflow_dispatchBuilds the Astro 6 + Starlight 0.39.x doc site and deploys to GitHub Pages at https://product-on-purpose.github.io/pm-skills/.

The validation.yml workflow contains the lion’s share of validator script invocations (~56 across both ubuntu + windows jobs). The validate-plugin.yml workflow contains ~12 plugin-specific script invocations. Together they cover the full enforcing surface.

codeql.yml, deploy-pages.yml, release.yml, and release-zips.yml do not invoke pm-skills validators - they do specialized jobs (security scanning, doc-site build, release publishing).

Validators live in scripts/. Most have three sibling files: {name}.sh (POSIX bash), {name}.ps1 (PowerShell), and {name}.md (documentation). This is the canonical .sh + .ps1 + .md convention.

Enforcing Validators (truly block CI on failure)

Section titled “Enforcing Validators (truly block CI on failure)”

These run in validation.yml and fail the build if they exit non-zero.

ValidatorWhat It Catches
lint-skills-frontmatterSkill YAML frontmatter consistency (name, classification, version, license, metadata, phase when applicable)
validate-agents-mdAGENTS.md skill paths + sub-agent names in sync with skills/ and agents/ directories (extended in v2.16 to recognize sub-agent directory)
validate-commandsEach command file under commands/ references a valid skills/*/SKILL.md path
validate-meeting-skills-familyMeeting Skills Family contract enforcement (5 members; v2.11.0)
validate-foundation-sprint-skills-family --strictFoundation Sprint Family contract enforcement (7 members; v2.15.0)
validate-design-sprint-skills-family --strictDesign Sprint Family contract enforcement (7 members; v2.15.0)
check-rendered-links (post-build)Every internal link in the BUILT site (site/dist/) resolves to a real route, and every #fragment resolves to a real element id (anchors enforced via STRICT_ANCHORS=1 in CI; advisory otherwise). The build-aware successor to the retired filesystem link validators: scripts/remark-resolve-links.mjs emits Starlight slug links + GitHub source URLs at build, and this gate proves zero are broken
check-route-parity (post-build)No published URL silently disappears. Compares the built route set (site/dist/**/*.html) against the committed baseline scripts/route-manifest.txt and fails if any baseline route is missing (a removed/renamed URL is a “Site not found” for existing links/bookmarks). Added routes are allowed. After an intentional route change, add a redirect (keeps the route present) or regenerate the baseline with node scripts/check-route-parity.mjs --update and commit it with a reason
validate-docs-frontmatter --strictAstro Starlight frontmatter shape on hand-authored docs in site/src/content/docs/ (generated subtrees auto-skipped)
check-no-body-h1 --strictNo body H1 duplications (Starlight derives H1 from title frontmatter; body H1 would duplicate)
check-count-consistencySkill/command/workflow counts in tracked .md, .mdx, and .json match filesystem state, including the badge/skills-<N> shields-badge form (.mdx + badge added v2.19.0)
check-skill-cross-referencesBacktick skill-name references in skills/*/SKILL.md resolve to a real skills/*/ directory; intentional forward-refs are allowlisted (v2.19.0)
check-skill-page-sectionsEvery ## section in a SKILL.md renders on its generated docs page. Output-based: it imports renderSkillPage from scripts/gen-site.mjs, renders each page, and fails if any section is missing. Guards the silent-section-drop class that shipped ~27 hollow skill pages before v2.29.1
check-sample-countsThe on-disk library sample + sampled-skill counts match the headline numbers in README_SAMPLES.md and the site samples landing page (check-count-consistency excludes library/, so this closes that gap; v2.29.0)
(generated content: completeness-guarded)Reference pages are emitted by scripts/gen-site.mjs into site/src/content/docs/ and are gitignored + rebuilt each build, so there is no committed drift surface; content completeness is guarded by check-skill-page-sections (above)
check-landing-page-counts --strictLanding-page total count claims (site/src/content/docs/index.mdx, skills/index.md, etc.) match filesystem state
check-workflow-generator-coverageEvery workflow source has both an individual page and an index-table row
check-agents-md-command-syncAGENTS.md command table is in sync with commands/ directory
validate-script-docsEvery scripts/*.sh + *.ps1 pair has a companion *.md doc (promoted to enforcing v2.19.0, FU-8)

Advisory Validators (informational; do not block)

Section titled “Advisory Validators (informational; do not block)”
ValidatorWhat It Surfaces
check-mcp-impactDetects pm-skills changes that may require pm-skills-mcp companion updates (M-22 maintenance-mode posture; never blocking)
check-frontmatter-yamlYAML parse validity in frontmatter (sibling to lint-skills-frontmatter; finer-grained on YAML errors)
check-generated-freshnessGeneration-timestamp recency check
check-version-referencesVersion-string consistency across docs
check-workflow-coverageOlder variant of workflow-generator-coverage; complementary checks
check-em-dashesEm-dash + en-dash sweep (CLAUDE.md hard rule enforcement)
BundleWhat It Runs
pre-tag-validate.{sh,ps1}The full enforcing validator suite + a configurable advisory bundle. Recommended before any release-prep commit.
embed-skills.js (in pm-skills-mcp)Companion script in the pm-skills-mcp repo; reads pm-skills skills/ directory and embeds skill metadata into the MCP server. NOT a validator but mentioned here because it consumes pm-skills validator output indirectly.
WorkflowLocal Equivalent
validation.ymlbash scripts/pre-tag-validate.sh (or .ps1 on Windows)
validate-plugin.ymlbash scripts/validate-plugin.sh if present, else individually invoke validate-commands.sh + frontmatter checks
codeql.ymlRun CodeQL CLI locally per CodeQL docs
deploy-pages.ymlnpm run build (Astro production build)
OthersManually inspect the workflow_dispatch trigger inputs; some have no local equivalent

Windows local-run notes:

  • Node on PATH. In Git Bash on Windows, Node is not always on PATH. If cd site && npm run build or the node-based checks (gen-site.mjs, verify-edit-links.mjs, check-rendered-links.mjs) fail with node: command not found, prepend Node for the session: export PATH="/c/nvm4w/nodejs:$PATH" (Node 24). The bash validator scripts do not need Node; only the build + generator + link-check steps do.
  • Line endings. .gitattributes (added v2.19.0) pins *.sh to LF so Git Bash runs them without CRLF false-reds, regardless of your core.autocrlf setting. *.ps1 is checked out as CRLF; *.mjs/*.js/*.json as LF.

When adding a new validator script:

  1. Create the script trio: scripts/{name}.sh + scripts/{name}.ps1 + scripts/{name}.md. The .md documents what the validator catches, how to run it, and the failure-mode examples.
  2. Wire it into validation.yml (or validate-plugin.yml if plugin-specific). Add it to BOTH the ubuntu-latest job and the windows-latest job.
  3. Wire it into scripts/pre-tag-validate.{sh,ps1} if it should be part of the enforcing pre-release suite.
  4. Update this document’s validator catalog (the relevant section above) with the new entry.
  5. Add a test case to docs/internal/release-plans/v{next-version}/testing-summary_v{next-version}.md if it would be useful to include in the next release’s testing investment record.
Failure SymptomFirst Place to Look
validation.yml red, ubuntu-latest onlyLinux-specific shell-script issue (e.g., bash version, line ending)
validation.yml red, windows-latest onlyPowerShell-specific issue (e.g., encoding, CRLF parsing, -p flag)
validate-plugin.yml redPlugin manifest schema; check .claude-plugin/plugin.json validity + commands paths
check-rendered-links redA link in the built site does not resolve; check the source link and how scripts/remark-resolve-links.mjs maps it (in-site slug vs GitHub source URL). If it names a broken #anchor, the heading id moved or the fragment is stale
check-route-parity redA previously-published URL is gone from the build; restore it, add an Astro redirect (keeps the route present), or - if the removal is intentional - run node scripts/check-route-parity.mjs --update and commit the new scripts/route-manifest.txt with a reason
validate-docs-frontmatter --strict redAstro Starlight schema requires title: + ASCII colons in description; check the offending doc
check-no-body-h1 --strict redA body # Header line exists in a hand-authored page; remove or convert to ##
npm run build red (generator)scripts/gen-site.mjs failed or emitted invalid frontmatter; run cd site && npm run build locally and read the Astro error
check-count-consistency redA skill/command/workflow count claim in tracked .md is out of date; search for the stale number and update
Doc versionDateChange
1.0.02026-05-17Initial publication; consolidates the CI workflow + validator catalog that was previously scattered across validation.yml comments, individual scripts/*.md files, and release-plan testing summaries.