Skip to content

Set up citations in one repository

Your pages and the code they describe live in one repository. This page wires manni cite into it once, so that a bare manni cite check covers the right pages and finds the right sources. That is the whole of the setup for a single repository. There are two more pages beside this one for the repositories that need more.

Get started teaches the loop, meaning pin, check, read the finding, fix, re-pin. This page is the repository around that loop.

Every transcript below is real output from the built tool.

ThingWhere it comes from
Which pages are checkedA collection in manni.config.yaml, read by every manni tool.
Where source.file resolves fromThe git root, unless you say otherwise. One repository rarely says otherwise.
Where an entry is writtenThe page’s frontmatter, unless a collection declares a sidecar manifest.
Which findings fail the buildThe default severities, unless the team moves one.
  1. Declare the pages as a collection. collections: is a top-level key of the family’s one config file, not a cite: key. Every tool reads it, so the pages you validate metadata on are the pages you check citations on:

    manni.config.yaml
    collections:
    - name: docs-pages
    paths: ["docs/**/*.md"]
    exclude: ["docs/drafts/**"]

    paths and exclude are globs, relative to the config file. With this in place, manni cite check with no arguments covers every declared collection, and --collection docs-pages narrows a run to one of them. Named paths on the command line beat both.

    Every key of a collection is in the metadata tool’s configuration reference. The cite: keys, all seven of them, are in the configuration reference for this tool.

  2. Leave the root alone. A source.file is a path relative to one root, settled once per run. In a single repository that root is the git root above your working directory, which is what you want, so there is nothing to set. cd docs && manni cite check reaches the same sources as a run from the top.

    You find out when the root is wrong, because the source end reads missing rather than resolving to the wrong file:

    ✗ docs/limits.md
    ✗ fetch-timeout :16 current lib/limits.ts:2 missing
    Run manni cite remove --only fetch-timeout, then manni cite add against the file's new path.
    manni:cite source-missing: The path names no tracked file, or an encrypted source cannot be decrypted.
    Guide: https://hawkeyexl.github.io/manni/cite/fix/#source-missing
    1 file checked, 0 passed, 1 failed, 1 finding

    Where git is available, sources resolve through tracked files only, from git ls-files, under a realpath containment check. So a file you have not committed is missing too. Without git, a directory walk stands in, and an uncommitted file resolves. Where sources resolve from has the full order, and --root is how a second checkout is reached.

  3. Mint the first citation. add takes the page with its lines, then the source with its lines. --dry-run prints the diff and writes nothing:

    Terminal window
    manni cite add docs/limits.md:6 lib/limits.ts:2 --id fetch-timeout --dry-run
    --- docs/limits.md
    +++ docs/limits.md
    @@ -2,2 +2,12 @@
    title: Limits
    +citations:
    + - id: fetch-timeout
    + claim:
    + lines: 3
    + integrity: sha256-921b21cccab21a4577f224ec4171aa56a3414bb3a5a4704ab8b6f314c46aa094
    + source:
    + file: lib/limits.ts
    + lines: 2
    + integrity: sha256-78af1d3321f9cbb177a7e4c958e39be56fd14cb93c1e441778bc4232e0fe4b1f
    + commit-sha: 9265563d8ea84a13fea0001b7e531c119f47d070
    ---

    Drop --dry-run to write it. The report names both pins and the line each end sits on:

    docs/limits.md: added fetch-timeout to frontmatter (claim at line 16, sha256-921b21cc…; source lib/limits.ts:2 "export const FETCH_TIMEOUT_MS = 10_000;", sha256-78af1d33…, 9265563)

    You typed line 6 and the report says line 16, because the entry pushed the sentence down by ten lines. The entry itself stored lines: 3, which counts the page body rather than the file, so editing the frontmatter never moves a claim. Body lines and file lines states the rule once.

  4. Run the first check. With the collection declared, no arguments are needed:

    Terminal window
    manni cite check
    ✓ docs/limits.md
    ✓ fetch-timeout :16 current lib/limits.ts:2 current
    1 file checked, 1 passed, 0 failed, 0 findings

    Both ends read in file lines, whatever lines the entry stored. -q hides the clean files once the corpus is larger than a screen. Read a result decodes the row, and Fix a failing check decodes every status it can carry.

  5. Commit the page and the config, then gate it. manni cite check is something CI runs on every push, next to the metadata gate. Run it in CI has the workflow, the output formats and the exit-code contract. It also has the baseline, which lets an existing backlog ramp in rather than block the first pull request.

When nothing is declared and nothing is named

Section titled “When nothing is declared and nothing is named”

A run with no positional paths and no config is an operational error, exit 2, rather than a silently empty pass:

Terminal window
manni cite check --no-config
manni: No files to check. Pass paths/globs, or declare a collection under `collections:` in manni.config.yaml.

That is the metadata tool’s shared input model, which check and update both follow. add is the exception, because it takes one page and one source rather than a set.

An entry anchors its sentence in one of two ways. Claim lines name the sentence by its body lines. A marker is a comment in the body that names the entry. Either way the entry itself stays in frontmatter or a sidecar. The choice is per entry, so one page can use both.

Neither anchor always wins. They differ in what a prose edit costs:

When the prose changesClaim linesA marker
Text above the sentence is added or cutclaim-moved, a notice. manni cite update rewrites the lines.Nothing to do. The marker moves with its sentence.
The sentence is rewordedclaim-changed. update --accept re-pins the text at the recorded line.claim-changed. update --accept re-pins what the marker anchors.
The sentence is reworded and movedclaim-changed, and the recorded line holds other text. cite add it again.claim-changed. update --accept re-pins it.
Two branches edit prose near the sentenceThe body holds no citation, so the merge sees only prose.The marker line sits among the edits, so the merge can conflict on it.

A marker also adds a line to the page source for every citation. On a page many people edit at once, those lines are where merges collide. Claim lines leave the body untouched. In a sidecar, they leave the frontmatter untouched too.

So markers suit a page whose sentences move and get reworded. Claim lines suit a page many people edit at once, with manni cite update after each merge. Anchoring has the full rule.

By default an entry goes into the page’s own frontmatter, and nothing in the body repeats it. Two things change that, and each has a page of its own.