Skip to content

Get started

This page takes you from one Markdown file to a structure check you can put in CI. You will lint a how-to that is missing a section, read what the finding says, fix the page, and see the run go green. Then you will ask the tool why it picked the template it picked, and move the paths into config.

Every transcript below is real output from the built tool, over files committed in this repository.

  • Node.js 24 or newer. Check with node --version.
  • A page that says what it is. Structure routing reads the type: frontmatter key. A page without one is skipped rather than failed. You can point the tool at a whole tree and lint only what has opted in.

manni lint ships in the same package as manni meta. If you have one, you have the other.

Terminal window
npx @hawkeyexl/manni lint --help

Before linting anything, ask what templates exist:

Terminal window
manni lint templates
Templates:
tgdp:how-to:1.6 — How-to guide (The Good Docs Project) types: how-to [builtin]
tgdp:tutorial:1.6 — Tutorial (The Good Docs Project) types: tutorial [builtin]
tgdp:reference:1.6 — Reference (The Good Docs Project) types: reference [builtin]
tgdp:concept:1.6 — Concept (The Good Docs Project) types: concept, explanation [builtin]
tgdp:troubleshooting:1.6 — Troubleshooting (The Good Docs Project) types: troubleshooting [builtin]
tgdp:release-notes:1.6 — Release notes (The Good Docs Project) types: release-notes [builtin]
tgdp:readme:1.6 — README (The Good Docs Project) types: readme [builtin]

Seven built-in templates, derived from The Good Docs Project at v1.6.0. The types: column is the vocabulary a page’s type: frontmatter may use to route itself. Nothing here needs configuring.

Here is the page. It declares type: how-to, so tgdp:how-to:1.6 is what it will be held to:

docs/rotate-key.md
---
type: how-to
---
# Rotate an API key
## Overview
Rotating a key replaces the old secret without interrupting live traffic.
## Before you start
You need an account with the `admin` role.
## Rotate the key
Issue the replacement, then retire the old one.
```bash
widget keys rotate --id abc123
```
  1. Run the check.

    Terminal window
    manni lint structure docs/rotate-key.md
    ✗ docs/rotate-key.md
    5:1 manni:lint/structure/missing-section Rotate an API key: Missing section "See also"
    1 file checked, 0 passed, 1 failed, 0 skipped

    Exit code 1.

  2. Read the finding. Four parts, left to right:

    PartWhat it tells you
    5:1Line and column. Line 5 is the # Rotate an API key heading, because the section that is incomplete is the one the heading opens.
    manni:lint/structure/missing-sectionThe rule id: manni:lint / the job / the rule. Durable, and the same string the GitHub annotation, the SARIF ruleId and the JUnit failure type carry.
    Rotate an API key:The section the finding is about, named by its heading.
    Missing section "See also"What to change.

    The how-to template asks for a See also section, and upstream does not mark it optional the way it marks Before you start. So this page is a how-to that does not say where to go next.

  3. Fix the page. Add the section:

    docs/rotate-key.md
    ## See also
    Read about key scopes and expiry.
  4. Re-run.

    Terminal window
    manni lint structure docs/rotate-key.md
    ✓ docs/rotate-key.md
    1 file checked, 1 passed, 0 failed, 0 skipped

    Exit code 0. That is the whole loop.

structure is one job. check runs every job the repository configures, and names them on stderr so the report cannot be read against the wrong set:

Terminal window
manni lint check docs/rotate-key.md
Checked: structure.
✓ docs/rotate-key.md
1 file checked, 1 passed, 0 failed, 0 skipped

structure is the only job, so the two commands report the same thing. check is the command for CI. It runs every job the configuration declares, and it carries no job’s own flags.

A page can be held to a template you did not expect, or to none at all. --explain prints the routing table and lints nothing:

Terminal window
manni lint structure docs/ --explain
▸ docs/rotate-key.md
· cli --template not given
· frontmatter-template no $template in frontmatter
· config-override no overrides configured
→ type tgdp:how-to:1.6 type: how-to -> builtin template
alignment
(page) ← "Rotate an API key"
overview ← "Overview"
before-you-start ← "Before you start"
task ← "Rotate the key"
see-also ← "See also"
1 file, 1 routed, 0 unrouted

Five rungs are tried in order. · is a rung that did not decide and → is the one that did. Here the page’s own type: how-to picked the built-in template, which is the ordinary case.

Under the rungs is the alignment, which is the pairing the matcher chose. Each row is one template rule and the section it took. Read it when a finding surprises you.

Point the tool at a tree and most files will not have opted in yet. Those are skipped, counted apart, and never mistaken for passes:

- docs/notes.md skipped: no type in frontmatter and no template resolved
✓ docs/rotate-key.md
1 file checked, 1 passed, 0 failed, 1 skipped

A page that declares a type: nothing serves is different. That is an assertion the tool could not honour, so it is a finding. The message suggests the nearest doctype it knows.

1:1 manni:lint/structure/unknown-type No template serves type "how-two". Did you mean "how-to"? Declare it on a template with "types:", then pass that file with --templates.

Declare the pages once, under the family-level collections: key that every manni tool reads, and the command loses its arguments:

manni.config.yaml
collections:
- name: guides
paths: ["docs/**/*.md"]
exclude: ["**/drafts/**"]
Terminal window
manni lint check

The same command now runs locally and in CI, over exactly the pages manni meta validate runs over. A collection’s exclude decides membership, so the drafts are not in the set at all rather than filtered out afterwards.

Add your own doctype shapes beside it whenever the built-ins stop fitting:

manni.config.yaml
lint:
templates:
- ./templates.yaml

A template in that file declaring types: [how-to] replaces the built-in how-to shape for the whole repository, and no page changes. You do not have to write one from scratch. manni lint templates infer writes a first template from a page that already has the shape you want:

Terminal window
manni lint templates infer docs/rotate-key.md --out ./templates.yaml
Wrote template "how-to" to ./templates.yaml

Write a template takes it from there, and the templates reference has every key.

You have linted one page, read a finding, fixed it, and moved the paths into config. To make it a standing guarantee, run the check on every push.