Skip to content

Coverage

Coverage answers one question. What can a consumer of the graph actually see? It counts the documents that carry each measured predicate and reports the share, so an under-annotated corpus is a number rather than an impression.

Terminal window
$ manni graph stats -c test/graph/fixtures/dd/docs.config.yaml
Coverage (documents):
title 3/3 100.0%
description 1/3 33.3%
creator 3/3 100.0%
created 3/3 100.0%
modified 3/3 100.0%
subject 2/3 66.7%
label 2/3 66.7%
language 0/3 0.0%
type 2/3 66.7%
applies-to 0/3 0.0%
about-product-lifecycle 0/3 0.0%
about-product-aspect 0/3 0.0%
sample-questions 0/3 0.0%

The list is fixed and deliberate rather than a census of what the corpus happens to contain. A predicate absent from every document still shows as 0.0%, which is the row that tells you something. A dynamic list would hide exactly that.

RowPredicateWhat fills it
titledcterms:titleThe page’s title frontmatter, or its first heading.
descriptiondcterms:descriptionThe page’s description frontmatter.
creatordcterms:creatorauthor or authors frontmatter, as names or person objects with a name, or the git commit history.
createddcterms:createdcreated frontmatter, else date, else the first git commit that touched the file.
modifieddcterms:modifiedThe first of last-updated, updated, lastmod and modified in frontmatter, else the last git commit.
subjectdcterms:subjecttags or keywords, plus graph.concepts or else page-level concepts. Each value becomes a SKOS concept node.
labelfoaf:primaryTopicgraph.label, the page’s preferred name for the concept it is about.
languagedcterms:languageA page’s own lang or language, else the nearest enclosing routes entry that declares one. A BCP-47 tag.
typeiirds:has-topic-typegraph.type, or a page type the derivation maps onto an iiRDS topic type.
applies-toiirds:relates-to-product-variantgraph.applies-to: which product variants this page is about.
about-product-lifecycleiirds:relates-to-product-lifecycle-phasegraph.about-product-lifecycle.
about-product-aspectiirds:has-subjectgraph.about-product-aspect.
sample-questionsgraph:sampleQuestionThe page’s sample-questions: the questions a reader would ask it.

Frontmatter in this table includes every field an external-metadata manifest holds for the page. A description kept in a {page}.meta.yaml fills the row as one on the page does.

language was dropped once, as near-universally zero noise, and restored when it became a scope dimension. An unlabelled document in a translated corpus cannot be filtered, indexed per locale, or counted against the right audience. The row is the only warning you get.

stats ends with the concepts no question reaches. A concept has a question when a page whose primary topic or subjects name it asks one. The list is reported and never gated, like orphans.

Terminal window
Concepts with no question (3):
Installation
onboarding
reference

not-applicable-to and not-about-product-aspect are absent from the table on purpose.

Coverage asks what a consumer can see that an author lifted. A document with no not-applicable-to is not under-annotated. Under open-world semantics its absence means unknown, which is the normal state for almost every page. Counting it as a gap would park every healthy corpus near zero on two rows. It would also teach readers to ignore the whole table.

The two are exported as UNMEASURED_BY_DESIGN from the library API, so the decision is enforced rather than written down.

Below the document table is a second one, over graph:Section nodes:

Terminal window
Coverage (sections, not gated):
type 0/8 0.0%
applies-to 0/8 0.0%
about-product-lifecycle 0/8 0.0%
about-product-aspect 0/8 0.0%
subject 0/8 0.0%

Sections are explicit-only. A section gets exactly what its own block declares and inherits nothing from its document, so these numbers are low by construction on most corpora. That is what makes them useful. The granularity rule says content granularity must match node granularity. This is the number that says whether it does, and a corpus whose pages each cover several unrelated topics shows it here first.

label is deliberately absent from this table. A preferred label at section level was judged meaningless, so it cannot be missing.

stats alone always exits 0. --check makes it a gate: it exits 1 when a broken internal link exists, or when any measured row is below its threshold.

The threshold lives in config, so every machine and CI agree:

graph:
stats:
coverageThreshold: 80

A single number applies to every measured row but sample-questions. That row is gated only when a map names it, so a gate that passed before the row existed still passes. A map sets them one at a time, which is what a real corpus needs, because title should be at 100 long before about-product-aspect is above nothing:

graph:
stats:
coverageThreshold:
title: 100
description: 90
created: 100
modified: 100
subject: 60
type: 60

Only measured names are accepted; anything else is a config error naming the key. --coverage-threshold <pct> overrides the whole map for one run, which is how you find today’s floor before committing to one.

A row below its threshold is marked in the report:

Terminal window
about-product-aspect 0/3 0.0% ! below threshold

The useful move is the one the metadata tool’s rollout page describes. Measure today, set each threshold just below where the corpus already stands, and raise it as graph fill and human review close the gap. A threshold set at the number you wish were true fails every build from day one. The pressure that follows is to delete the gate rather than to fill the fields.

Terminal window
$ manni graph stats --check --coverage-threshold 50

Run that with no config threshold to find the honest starting line, then write the map.

  • Hand-written frontmatter, for anything a person knows and a machine cannot guess: which product variant a page is about, which concept it owns.
  • manni graph fill, for a corpus nobody annotated, with every written value recorded for review.
  • Git, for creator, created and modified, which are detected rather than declared and therefore need no author at all.