Skip to content

Adopt in a repo

This walkthrough takes a fresh repository from zero to full jk-standards gating. If you already have pre-commit, jk-standards.yaml, and a drift map, jump straight to the Quickstart instead.

Decide where your docs live. Common shapes:

  • docs/ for a plain markdown repo
  • site/src/content/docs/ for an Astro/Starlight site
  • Both, if you have some raw markdown and a rendered site

Every file under a doc root will need class: frontmatter. If you have long-frozen historical docs you don’t want to classify, list their containing directory under exempt_dirs.

Create jk-standards.yaml at the repo root. The toolkit dogfoods itself, so its own config is the reference example:

version: 1

doc_roots:
  - path: docs
    extensions: [".md"]
  - path: site/src/content/docs
    extensions: [".mdx"]

taxonomy:
  classes: [generated, gated, archived]

file_line_refs:
  source_roots:
    - path: src
      extensions: [".py"]

# Inventory facts about the toolkit itself (how many checks, hooks, skills)
# must not be restated as numerals in the docs — they change as checks land.
# region:count-triggers
count_drift:
  triggers:
    - 'checks?\s+(?:in\s+total|total)'
    - 'hooks?\s+(?:in\s+total|total)'
    - 'skills?\s+(?:in\s+total|total)'
    - 'checks?\s+ship(?:s|ped)?'
    - 'hooks?\s+ship(?:s|ped)?'
    - 'skills?\s+ship(?:s|ped)?'
    - 'fields?\s+(?:in|of)\s+(?:the\s+)?config'
    - '(?:checks?|skills?|hooks?|fields?)\s+are\s+available'
    - '(?:checks?|skills?|hooks?|fields?)\s+are\s+registered'
# endregion:count-triggers

drift_map: .github/docs-drift-map.yml

behavioral_claims:
  sources:
    - type: pytest
      path: tests

# Site JSON fixtures projected from Python source. `generated-freshness`
# runs each command, diffs against the tracked file, and restores the
# snapshot — so a source change without a regenerated fixture fails CI.
#
# coverage.json is intentionally NOT gated here: coverage numbers are
# environment-dependent (Python version, platform branches) and would drift
# between local dev and CI even with no source change. The emitter still
# runs at site-build time (site/package.json prebuild) so the site always
# shows current coverage; it just isn't diff-gated.
# region:generated-fixtures
generated:
  - doc: site/src/generated/checks.json
    command: jk-standards emit checks
  - doc: site/src/generated/config-schema.json
    command: jk-standards emit config-schema
  - doc: site/src/generated/skills.json
    command: jk-standards emit skills
# endregion:generated-fixtures
jk-standards' own config — the toolkit gates itself with itself.

Field-by-field detail is in the Configuration reference.

Frontmatter for every file under the doc roots:

---
class: gated
title: My doc
---
# ...

The three canonical classes:

  • gated — living, current-state doc. Linted by status-prose, file-line-refs, count-drift, and behavioral-claims.
  • archived — frozen dated record. Exempt from all linting; the content is a historical artifact.
  • generated — machine-produced. Freshness-checked against its generator command, but not prose-linted.

Run jk-standards doc-taxonomy — it lists every doc missing a class or carrying an unknown one.

Add jk-standards to your .pre-commit-config.yaml, pinned to a release tag. Every shipped hook is defined once in .pre-commit-hooks.yaml — this is the exact definition your pre-commit install will resolve when you enable doc-taxonomy:

- id: doc-taxonomy
  name: docs carry a lifecycle class
  entry: jk-standards doc-taxonomy
  language: python
  types_or: [markdown, mdx]
  pass_filenames: false
One shipped hook — consume from a pinned tag.

Enable the ones that fit your repo:

- repo: https://github.com/JimAKennedy/jk-standards
rev: v0.1.0
hooks:
- id: doc-taxonomy
- id: status-prose
- id: file-line-refs
# opt in when you have inventory nouns to gate:
# - id: count-drift
# opt in when you have a test suite to cite:
# - id: behavioral-claims

Then pre-commit install and pre-commit run --all-files to catch existing violations.

doc-drift needs a git base ref (the PR merge base) and full commit history, so it lives in CI rather than pre-commit. Consume the shipped reusable workflow:

.github/workflows/docs.yml
name: docs
on: [pull_request, push]
jobs:
discipline:
uses: JimAKennedy/jk-standards/.github/workflows/doc-discipline.yml@v0.1.0
with:
toolkit-ref: v0.1.0 # pin the same version you used above
config: jk-standards.yaml # path in your repo

The workflow checks out the PR with fetch-depth: 0, installs jk-standards at the pinned ref, and runs jk-standards all. First-run failures name the exact drift; fix by either updating the doc or adding a Docs-Not-Affected: <reason> trailer to a commit in the range.

Step 6 (optional) — Enable inventory gating

Section titled “Step 6 (optional) — Enable inventory gating”

If your prose says things like “N presets ship” or “N chapters in the guide”, configure count_drift.triggers and interpolate the number from a generated JSON:

count_drift:
triggers:
- 'factory\s+presets?'
- 'chapters?\s+in\s+total'
generated:
- doc: docs/counts.json
command: node scripts/emit-counts.mjs

Then reference it in prose as {counts.presets} rather than a numeral — the count-drift check refuses numerals adjacent to trigger phrases, but leaves the template literal alone.

Step 7 (optional) — Enable behavioral claims

Section titled “Step 7 (optional) — Enable behavioral claims”

If you want prose to cite tests, configure a test-index source:

behavioral_claims:
sources:
- type: pytest # gtest | js | pytest
path: tests

Then in prose, add a verified: marker after each claim. The marker wraps a key from the scraped test index in square brackets — pytest keys look like test_module::test_name, gtest keys look like Suite.TestName. Unresolved citations fail; the ⚠ unverified marker is allowed but counted as an honest-state metric.

The Checks reference uses citations like these throughout — every claim about the toolkit’s behavior points at a real test.