Write manni metadata from another tool
A link checker, a test runner or a sample runner learns something about a page each time it runs. When it writes that result into the page, every manni tool can read it. That holds only when the write follows the family’s rules. This page states those rules for a tool author, and for whoever wires the tool into a docs repository.
Each rule links to the reference that defines it. The vocabularies are the contract, and this page says how to honor them.
The rules
Section titled “The rules”- Read the vocabulary first. Field names, types and marks live in the
schema, not in this page.
manni meta schemaslists every built-in, andmanni meta schemas show <id>prints one with each field’s type and description. Each field also carries anx-manni-locationmark and, where it matters, anx-manni-graph-outputmark. - Write a field where it lives. A
pagefield goes in the page’s front matter. Anexternalfield goes in the manifest the collection’sexternalMetadatanames. A tool that cannot readmanni.config.yamlwritespagefields only. It leavesexternalones tomanni meta relocate. - Attribute what you write. A measured fact goes in its field’s
companion, such as
verified-bybesidelast-verified, orreviewed-bybesidelast-reviewed. A value proposed for a person to review goes in ameta-provenanceentry, withgenerated-byand the field pointers. Never writeprovenance, whichmeta derivemanages. - Never write a derived or managed field.
manni meta derivemanagescreatedandlast-updated.manni cite updatemanages a citation’sclaim,source.integrityandsource.commit-sha. A second writer for any of them is a value two tools fight over. - Write dates as UTC days, or as RFC 3339 with a timezone.
2026-10-21and2026-10-21T14:02:11Zboth pass. Never write a local time with no offset, and never a month alone. - Write on success only. A failed check leaves its value alone. The age of the last good value is the honest signal, and a reader sees it.
- Write without churn. Write only when a value changes, so a second run leaves the file as it was. Keep the block’s YAML style and key order, and add a new key at the end of its block. A value that would differ on two branches for the same content is not written per branch. That is the rule merge-safe derivation follows too.
- Validate after writing. Run
manni meta validate <file>in the same job, and fail the job on exit1. Validation checks the form of every value the vocabularies define. - Write a pin’s result on its citation entry. A test that runs the cited
code records
verified-byandlast-verifiedon that entry, as the citations vocabulary defines them. It never touches the entry’sclaimorsource. - Follow the worked example. Doc Detective’s write-back, below, applies every rule on this list to one page.
- Write a fact about one block as a statement. A
metastatement sits above the block it describes, in the page’s own comment syntax. Change only your own entry in it. Pin the block by the hashing rule, withpinOffrom@hawkeyexl/mannior the same rule in your own language. Before hashing, cut every statement inside the block’s lines out of the text, as manni does. That covers the statement you write and any other one, such as a step’s own statement inside a whole procedure. A<data name="manni-meta">carrier goes whole, from its start tag to its end tag. Then drop each line the cut left blank, and keep the lines that were blank already. Keep the statement’s comment form, so the write is a one-line diff. Write it on success only, as rule 6 says.
A verification, end to end
Section titled “A verification, end to end”The stewardship vocabulary
defines two fields for a check’s result. verified-by lists the checks that
verify a thing. last-verified is the date by which every one of them had
passed. They go in a statement on the block a check verifies, or in front
matter when the check covers the whole page.
On one procedure
Section titled “On one procedure”The author declares the check in a statement above the procedure. The entry names the tool, and the test id the tool looks for:
## Install
<!-- meta {"verified-by":[{"tool":"doc-detective","check":"install-steps"}]} -->1. Run `npm install --save-dev @hawkeyexl/manni`.2. Run `npx manni meta validate docs/`.CI runs the tests, and the test passes. Doc Detective finds the statement
whose entry names it, and fills in that entry. It sets last-verified, since
every listed check has now passed. Then it pins the two steps it proved and
writes the line back in its own form:
<!-- meta {"verified-by":[{"tool":"doc-detective","check":"install-steps"}]} --><!-- meta sha256-384b6729be777c15e9e55ae3f0a0fb1fbe63793abf601e5f517702e9019966e3 {"verified-by":[{"tool":"doc-detective","check":"install-steps","version":"4.27.0","passed":"2026-10-21"}],"last-verified":"2026-10-21"} -->1. Run `npm install --save-dev @hawkeyexl/manni`.2. Run `npx manni meta validate docs/`.Each rule shows in that diff. Both fields are page fields, so they stay in
the page (rule 2). The tool’s version and the date sit in verified-by, the
companion of last-verified (rule 3). The date is a UTC day (rule 5). A
failing run writes nothing (rule 6). New keys go at the end of their objects,
and a second passing run on the same day changes nothing (rule 7). The pin
hashes the two steps by the hashing rule, and the comment form is unchanged
(rule 11).
The job then validates the page (rule 8), and any manni tool reads the statement back:
manni meta validate docs/install.mdmanni meta get title docs/install.md✓ docs/install.md
1 file checked, 1 passed, 0 failed, 0 errorsdocs/install.md: title=Install manni (asserted)docs/install.md: statement at line 9 (lines 10-11, current) {"verified-by":[{"tool":"doc-detective","check":"install-steps","version":"4.27.0","passed":"2026-10-21"}],"last-verified":"2026-10-21"}A later edit to either step makes validate warn statement-changed, until
the next passing run pins the new text. A procedure on the same page with no
statement claims nothing.
On a whole page
Section titled “On a whole page”When the check covers the whole page, the same entry goes in front matter:
---title: Set up citedescription: "Pin your first sentence to its source and gate it in CI."last-updated: 2026-10-17verified-by: - tool: doc-detective check: cite-set-up---On a pass, Doc Detective writes the same values into that block:
last-updated: 2026-10-17verified-by: - tool: doc-detective check: cite-set-up version: 4.27.0 passed: 2026-10-21last-verified: 2026-10-21last-updated is untouched, since derive owns it (rule 4). The new keys go
at the end of their blocks (rule 7).
test/fixtures/verification/one-entry.md holds that page after the write.
The job then validates it (rule 8):
manni meta validate test/fixtures/verification/one-entry.md✓ test/fixtures/verification/one-entry.md
1 file checked, 1 passed, 0 failed, 0 errorsAny manni tool reads the values back as written:
manni meta get test/fixtures/verification/one-entry.md --fields last-verified,verified-bytest/fixtures/verification/one-entry.md: last-verified=2026-10-21 (asserted)test/fixtures/verification/one-entry.md: verified-by=[{"tool":"doc-detective","check":"cite-set-up","version":"4.27.0","passed":"2026-10-21"}] (asserted)A tool that writes a local time with no offset fails at that line, and exits
1. The guidance line quotes the date rule:
manni meta validate test/fixtures/verification/bad-no-timezone.md✗ test/fixtures/verification/bad-no-timezone.md /verified-by/0/passed must match pattern "^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])(?:T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9]))?$" (line 8) [manni:stewardship:1.2.0] manni:stewardship:1.2.0 verified-by/passed: A day such as 2026-10-21, or a timestamp with a timezone such as 2026-10-21T14:02:11Z. Guide: https://hawkeyexl.github.io/manni/meta/reference/schemas/manni-stewardship/
1 file checked, 0 passed, 1 failed, 1 errorWhen more than one tool verifies a page
Section titled “When more than one tool verifies a page”Each tool gets an entry of its own, and writes only that entry. A link checker added beside Doc Detective looks like this:
verified-by: - tool: doc-detective check: cite-set-up version: 4.27.0 passed: 2026-10-21 - tool: lycheeAn entry with no check means the tool verifies the whole page however it
does. When the link check passes on 2026-10-22, its writer sets
passed: 2026-10-22 on its own entry. last-verified stays 2026-10-21, the
oldest pass. That is the day by which every listed check had passed, so the
date never claims more than the checks proved.
manni writes neither field. It defines them, validates their form, and carries them into a graph. Record when a page was last verified shows the same flow from the author’s side.