Configuration reference
manni graph reads one file, manni.config.yaml, and one key in it, graph:. Two
top-level keys beside it belong to the family rather than to this tool.
collections: says which documents every tool reads. providers: says how
every tool reaches a model.
Discovery walks from the working directory up to the repository root. -c
names a file instead, --no-config ignores whatever discovery found, and with
no file at all every value below is its built-in default.
Inside graph: unknown keys are refused, so a typo is caught rather than
silently ignored:
$ manni graph buildmanni: Invalid config in manni.config.yaml: /graph/fill: unknown key "maxCostUsd"The whole file
Section titled “The whole file”# The family's document sets. Not a graph key: every tool reads these.collections: - name: site paths: - "docs/**/*.{md,mdx}" exclude: - "docs/drafts/**"
# The family's inference settings. Not a graph key either.providers: provider: anthropic
graph: baseIri: https://example.com/graph/ out: graph/graph.ttl authoring: true
routes: - basePath: / root: docs extensions: [".md", ".mdx", ".markdown", ".html", ".htm", ".adoc", ".asciidoc", ".rst", ".dita", ".ditamap", ".flsnp", ".fltoc", ".flbrs"] indexFiles: ["index", "README"] language: en
build: derive: [frontmatter, sections, links, tags, images, code, provenance, terms]
check: shapes: [] questions: top: 5 severity: warning
provenance: qualified: true
stats: coverageThreshold: 80
fill: temperature: 0 maxTurns: null cacheDir: .manni/graph/cache confidenceThreshold: 0.7 writeProvenance: true validateGraph: true sections: false fields: [label, alt-labels, broader, narrower, related-concepts, concepts, type, applies-to, about-product-lifecycle, about-product-aspect, not-applicable-to, not-about-product-aspect, sample-questions] chunkChars: 12000 offline: false questions: perPage: 3
embed: model: onnx-community/granite-embedding-small-english-r2-ONNX dtype: q8 out: graph cacheDir: .manni/graph/embed-cache byLanguage: de: model: Xenova/bge-small-en-v1.5
export: iirds: title: Product documentation creator: Example Corp version: "1.3" chunks: evals: .manni/evals.json llms: title: Product documentation summary: Everything the product does, page by page. origin: https://docs.example.com
retrieve: seeds: 3 depth: 1 maxChars: 12000 withhold: []
serve: maxChars: 40000Section keys are camelCase (baseIri, maxTurns, confidenceThreshold). The
page vocabulary a document writes is kebab-case (alt-labels, applies-to).
The two are different surfaces and spell things differently on purpose.
Where the documents come from
Section titled “Where the documents come from”collections: is the family’s, declared once and read by every tool. See
the metadata tool’s configuration reference
for its full shape.
| Field | Type | Default | What it does |
|---|---|---|---|
name | string | required | What --collection <name> selects. |
paths | list of globs | required | Members, resolved from the config file’s directory rather than from where the command runs. |
exclude | list of globs | [] | Non-members. It decides membership, so an excluded file is not in the collection even when a positional path names it. |
url | string | none | Where those pages are published. manni a11y check reads it; graph does not. |
externalMetadata | list | [] | Frontmatter kept outside the document. build reads each page merged with it, as manni meta validate does, so an external graph block derives exactly as an inline one does. fill writes a key a manifest owns to that manifest. |
A build or fill run with no positional paths reads every
declared collection. --collection narrows that to one, and cannot be combined
with paths.
Nothing matching is exit 2:
$ manni graph buildmanni: No input files matched: docs/**/*.rst (cwd: /repo). Fix the globs, or pass --allow-empty if matching nothing is expected.Where the model comes from
Section titled “Where the model comes from”providers: is the family’s too. manni graph fill is the one verb that reaches
a model, and it resolves one in this order, later winning:
providers.provider, the family default, with the connection settings for each provider beside it.graph.provider, this tool’s override.--provider, this run’s.--local, over all three, runningllama-cppon this machine.
Unset everywhere, the provider is auto: an Anthropic key, then an OpenAI key,
then the Claude CLI, then a local model, detected exactly as manni meta fill
detects. A model is carried only to the provider its own level names, because a
model name does not say which provider owns it. Under auto a model is a usage
error.
graph keys
Section titled “graph keys”Top level
Section titled “Top level”| Key | Type | Default | What it does |
|---|---|---|---|
provider | string | unset | Which provider graph fill sends pages to: auto, anthropic, openai, claude-cli, llama-cpp. An unknown name is refused when the config is read, by every verb, not only the one that reaches a model. |
model | string | unset | The model within the provider named beside it. Unset, the inference library’s default for that provider is used; manni pins none, and every cache key names the model it resolved to. This is not embed.model. |
baseIri | string | urn:manni:graph: | The base every minted node IRI hangs off. A value not ending in / or : gets a trailing slash. Use an HTTP base when the graph is published, so its nodes dereference. |
out | string | graph/graph.ttl | Where graph build writes the Turtle, and where every graph reader looks for it. Resolved from the working directory. -o overrides it for one build, -g for one read. |
authoring | boolean | true | Whether a build carries author-facing content: the strategy records, typed, and every value a page’s schemas mark x-manni-location: external, at any depth. false leaves both out of every build, export and publish. --no-authoring sets it false for one run. See a graph for readers. |
questionFiles | list of paths | [] | Golden-set files, relative to the config file. They hold questions that span pages or that the docs don’t answer. graph check asks them beside each page’s sample-questions. A missing file, or one that fails its schema, stops check with exit 2. |
routes
Section titled “routes”Maps published site paths back to source files, so a link written as the reader
sees it resolves to the page that serves it. Each entry is an object; only
root is required.
| Key | Type | Default | What it does |
|---|---|---|---|
basePath | string | / | The site prefix this route serves. Normalized to a leading / and no trailing one; / means the site root. |
root | string | required | The directory that prefix resolves into, relative to the config file. |
extensions | list of strings | [".md", ".mdx", ".markdown", ".html", ".htm", ".adoc", ".asciidoc", ".rst", ".dita", ".ditamap", ".flsnp", ".fltoc", ".flbrs"] | Extensions to try, in order, when a route names a page without one. Markdown comes first, so a tree holding install.md and a built install.html resolves to the source. Each must start with a dot. |
indexFiles | list of strings | ["index", "README"] | Basenames to try for a directory route such as /guides/. |
language | BCP-47 tag | unset | The language of every document under root. A page’s own lang or language frontmatter wins; absent that, the nearest enclosing route that declares one applies. A MadCap Flare topic under no such route takes its project’s xml:lang. Unset, the route says nothing about language. |
Routes are corpus-defining, so they are config-only. There is no flag.
| Key | Type | Default | What it does |
|---|---|---|---|
derive | list | all eight | Which sources become triples: frontmatter, sections, links, tags, images, code, provenance, terms. Narrowing it makes a smaller graph, not a wrong one; a predicate nothing derives simply has no triples. terms reads the termbase manni term check reads, from the same config, and makes each term a concept. Every concept label then resolves through it first. A list that leaves terms out mints every concept from its label. |
| Key | Type | Default | What it does |
|---|---|---|---|
shapes | list of paths | [] | The SHACL shapes graph check validates the built graph against. Empty means the shapes shipped in the package, shapes/graph/shapes-1.4.0.ttl. Naming shapes here replaces the bundled set rather than adding to it, so list the bundled file too when you mean to extend it. --shapes overrides this for one run. |
questions.top | integer, 1 or more | 5 | How high a page must rank for each of its own sample-questions, or the section holding a pinned answer. graph check asks each question through the retrieval graph search uses, and reports one outside the top results. |
questions.severity | notice, warning, error or off | warning | The severity of a question that does not find its page or its answer, and of an answer whose lines changed. A question asked twice, and an answer whose page is gone, take it too. A drafted question or pin reports at notice instead. error fails the run. off reports none of these, and still reports a moved answer. |
questions.foundThreshold | number 0–100, or null | null | graph check exits 1 when the percentage of reviewed questions found falls below this. It is the same scale as stats.coverageThreshold. questions.severity does not touch it. null sets no rate gate. |
questions.citedThreshold | number 0–100, or null | null | graph check exits 1 when the percentage of reviewed questions graph retrieve cites falls below this. It applies only when a chunk artifact sits beside the graph. null sets no gate. See Cited by graph retrieve. |
graph: questionFiles: [docs/questions.yaml] check: questions: top: 5 severity: warning foundThreshold: 95The found rate counts reviewed questions that have a test: a page, or at least one pin. Gaps and drafts are left out. A question with several answers is found only when every answer is. A changed answer, or one whose page is gone, counts as not found.
The questions have no flag. They are asked in their page’s language, against
an index built from the graph in memory. A vector sidecar and an embedder
beside the graph add the vector leg, as they do for graph search.
provenance
Section titled “provenance”| Key | Type | Default | What it does |
|---|---|---|---|
qualified | boolean | true | Emit qualified PROV attribution and association nodes, which carry a role, rather than the bare prov:wasAttributedTo edge alone. |
Git itself is detected, never declared. History is used wherever git can run over a repository. A run that finds neither git nor a repository warns once with git’s own reason and builds the rest. There is no switch, because a switch for a detectable fact is one more way to be wrong. A job that needs the stronger contract writes it as a shape over the built graph.
| Key | Type | Default | What it does |
|---|---|---|---|
coverageThreshold | number 0–100, or a map | {} | The minimum share of documents that must carry each measured field. A single number applies to every measured field but sample-questions, which only a map gates. A map sets them one at a time, keyed by field name. Empty gates nothing. Enforced only under stats --check; --coverage-threshold overrides it for one run. |
There are thirteen measured document-level fields, and the Coverage page lists them with what fills each.
graph: stats: coverageThreshold: title: 100 description: 90 type: 60| Key | Type | Default | What it does |
|---|---|---|---|
temperature | number 0–2 | 0 | Sampling temperature for the proposal call. Zero is the default because a reproducible proposal is worth more than a varied one. |
maxTurns | integer ≥ 1, or null | null | Stop after this many inference calls. One page is one turn; a cached page spends none. null is unbounded. A page the budget stopped at is reported skipped, reason turn budget. |
cacheDir | string | .manni/graph/cache | Where raw proposals are cached. Caching happens before the confidence gate, so re-running at a different --confidence spends nothing. |
confidenceThreshold | number 0–1 | 0.7 | The self-reported confidence a proposed value must clear to be written. Below it the value is reported and not written. |
writeProvenance | boolean | true | Record what was written in the page’s meta-provenance, naming the model and the fields. A drafted question gets generated-by and confidence instead. Turning it off removes the review trail; leave it on. |
validateGraph | boolean | true | Vet each proposal against the SHACL shapes before writing it, simulating it in the derived graph. Accepted proposals fold into that state, so two pages in one run cannot jointly corrupt the graph. |
sections | boolean | false | Also propose per-section metadata, not only page-level. Opt-in, because it is a great deal more output to review. |
fields | list | every field | Which fields may be proposed at all: label, alt-labels, broader, narrower, related-concepts, concepts, type, applies-to, about-product-lifecycle, about-product-aspect, not-applicable-to, not-about-product-aspect, sample-questions. |
chunkChars | integer ≥ 1 | 12000 | The most characters of a page one question request sends. A longer body is split at line boundaries. The name, default and bounds match meta fill’s. --chunk-chars overrides it. |
offline | boolean | false | Send nothing off this machine, and run only a local model already downloaded. It means what offline means for meta fill and term fill. --offline overrides it. |
questions.perPage | integer ≥ 1 | 3 | fill drafts questions until a page has this many, reviewed or drafted. |
graph publish runs its embed step only when this section exists, even
empty. It runs the iiRDS export only when export.iirds exists.
| Key | Type | Default | What it does |
|---|---|---|---|
model | string | onnx-community/granite-embedding-small-english-r2-ONNX | The local embedding model. Any id is accepted: the documented table is the tested set, not the permitted one, so a newer model works without a manni release. |
dtype | string | q8 | Weight quantization. q8 keeps int32 accumulation, which is associative, and so keeps the sidecars reproducible. |
out | string | the index directory | Directory for the vector sidecars, one file per language. Resolved from the working directory. -o overrides it for one run. |
cacheDir | string | .manni/graph/embed-cache | Per-text vector cache, keyed on text, model and dtype. |
byLanguage | map, BCP-47 tag → {model, dtype} | {} | Per-language overrides. A German corpus embedded with an English-only model returns confident, meaningless vectors and fails nothing, which is why this key exists. Unlisted languages use the two settings above. |
Tested models, with what each costs:
| Model | Note |
|---|---|
onnx-community/granite-embedding-small-english-r2-ONNX | Default. 384 dimensions, an 8192-token context, so a section is never truncated. No prefixes. |
Xenova/gte-small | Lighter, about 34 MB. 384 dimensions, 512-token context. |
Xenova/bge-small-en-v1.5 | 384 dimensions. Needs a query prefix, applied automatically here, because omitting it degrades retrieval silently. |
Xenova/all-MiniLM-L6-v2 | Smallest, about 23 MB, but truncates at 256 wordpieces, so a long section loses its tail. |
export
Section titled “export”| Key | Type | Default | What it does |
|---|---|---|---|
iirds.title | string | the exporter’s own | Package title written into the iiRDS container. |
iirds.creator | string | unset | Creator organization, written as an iiRDS Party and a vCard organization. |
iirds.version | "1.2" or "1.3" | "1.3" | The iiRDS version literal the package declares. Quote it: unquoted, YAML reads 1.3 as a number and the value is refused. |
chunks.evals | path, or null | null | A docevals JSON report, as manni docevals run -f json writes it, relative to the config file. It fills each chunk record’s evals. null leaves every record’s evals null. --evals overrides it for one run. Eval verdicts are author-facing, so a run with authoring off does not read it, and warns once when it is set. |
llms.title | string | none | The H1 of llms.txt. graph export llms needs it. graph publish writes llms.txt only when the llms section exists. |
llms.summary | string | none | The blockquote under the H1. |
llms.origin | URL | none | The site’s scheme and host, such as https://docs.example.com. Each link is this plus the page’s route. graph export llms needs it. |
retrieve
Section titled “retrieve”The retrieval settings. graph lookup, graph retrieve and
graph traverse --context read them, and a flag on retrieve overrides each
one for a run. graph export chunks records the resolved values under the
manifest’s retrieve, the defaults a script’s createRetriever reads. None
of these keys is required.
| Key | Type | Default | What it does |
|---|---|---|---|
seeds | integer ≥ 1 | 3 | The number of top-ranked passages retrieval starts from. lookup returns this many matches. --seeds overrides it. |
depth | integer ≥ 0 | 1 | The number of graph hops retrieval follows out from those passages. lookup’s outward walk uses it. --depth overrides it. |
maxChars | integer ≥ 1 | 12000 | The most characters of passage text a bundle returns. On lookup it applies to each section on its own, and it only ever drops text, never a node or an edge. --max-chars overrides it. |
withhold | list of drifted, failing | [] | Applied at export. A matching passage is left out of the rows and listed under the manifest’s withheld. drifted matches a pin at error severity. failing matches a current eval verdict that failed or errored, and a stale verdict never counts. |
graph: export: chunks: evals: .manni/evals.json retrieve: withhold: [drifted, failing]See Chunks for the files the export writes.
What graph serve sends an agent over MCP. The key is config-only, and it
is not required. The CLI twins print the full result whatever it says.
| Key | Type | Default | What it does |
|---|---|---|---|
maxChars | integer ≥ 1 | 40000 | The most characters one served answer may take, as JSON. Past it, passages go out as references with no text, then edges go. What an answer carries gives the order. |
Keys that moved or went
Section titled “Keys that moved or went”Refused by name, with the replacement in the message, rather than aliased. An alias would be a second place to declare one thing.
| Key | What to write instead |
|---|---|
graph.inputs | A top-level collections: entry. Document sets are declared once for every tool. |
graph.exclude | The collection’s own exclude:, or --exclude for one run. |
graph.version | Nothing. The tool’s version is the package’s. |
graph.provenance.git | Nothing. Git is detected; a job that requires history expresses it as a shape. |
graph.fill.provider | graph.provider, or the family’s providers.provider. |
graph.fill.maxCostUsd | graph.fill.maxTurns. The budget is counted in inference calls, which every model makes and every model can be counted making. Converting turns to money is your provider’s rate card. |
kg.provenance on a page | The page-level meta-provenance, with JSON Pointers. The page block is graph: now. Unlike the keys above, a leftover kg: block is not refused. It validates and derives nothing. |
What a published graph carries
Section titled “What a published graph carries”Which fields reach an exported artifact is the schema’s call, not this tool’s.
Mark a top-level property x-manni-graph-output: false in the schema that governs
the page. It is then dropped before derivation, so it appears in none of the
four outputs. Absent means true. That holds whatever authoring says. The mark sits beside x-manni-location in the
same schema, so what a page stores and what a delivered artifact says are two
decisions in one place. graph export chunks reads both marks at every depth,
as Chunks describes.
graph keeps no schema set of its own. graph build reads the marks from the
set manni meta validate resolves for each page, so schemas, strict,
register and overrides under meta: decide them. The default set carries
manni:stewardship:1.2.0, which marks owner, stakeholders and
reviewed-by, so a published graph leaves those three out with no config line. See
Govern the graph and
how a schema reaches a file.