How collector taxonomy becomes integrations/taxonomy.json
Question answered: what is the general flow from collector
metadata.yaml and taxonomy.yaml to the generated dashboard
taxonomy artifact consumed by downstream frontend code?
Short version
metadata.yaml is the metric-context source of truth. Collector
taxonomy.yaml files organize those contexts into the dashboard table
of contents. integrations/gen_taxonomy.py validates both sides
against the taxonomy registries and schemas, then emits the gitignored
integrations/taxonomy.json cross-repo contract.
The implementation details can evolve, but the durable model is:
- metadata declares what metric contexts exist;
- taxonomy declares where those contexts belong and which widgets reference them;
- the generator proves the references are valid;
- the generated JSON carries the normalized section tree, placements, recursive items, and context snapshots.
Inputs
The taxonomy pipeline reads four source classes:
- Collector
metadata.yamlfiles. The generator loads collector modules through the shared integrations loader and extracts metric contexts frommetrics.scopes[].metrics[].name; seeintegrations/gen_taxonomy.py:269-276. - Collector
taxonomy.yamlfiles. These live next to collector metadata and use the closed v1 authoring schemaintegrations/schemas/taxonomy_collector.json. integrations/taxonomy/sections.yaml. This registry owns stablesection_idtargets and parentage for the generated TOC section tree; schema:integrations/schemas/taxonomy_sections.json.integrations/taxonomy/icons.yaml. This registry limits the icon IDs sections and placements may reference.
The field-level contract is documented in
../schema-reference.md. The contributor workflow is documented in
../recipes/add-go-collector.md and
../recipes/update-collector.md.
Metadata indexing
The generator first builds metadata indexes from all known collector metadata:
by_path_module: matches ataxonomy.yamlfile to its siblingmetadata.yamlmodule by path,plugin_name, andmodule_name.all_contexts: sorted global list of known metric contexts, used for prefix resolution.contexts_by_plugin: contexts grouped by plugin name, used forcollect_pluginselectors.- dynamic selector guardrails from
metrics.dynamic_context_prefixesandmetrics.dynamic_collect_plugins.
The relevant implementation is integrations/gen_taxonomy.py:286-315.
This is why metadata.yaml is the metric source of truth: a literal
context in taxonomy authoring is valid only if the sibling metadata
module declares it. A taxonomy file can organize and reference metric
contexts; it cannot invent static metric contexts.
Taxonomy authoring validation
Each collector taxonomy.yaml is loaded and validated against the
closed authoring schema before semantic validation. The schema rejects
old or ambiguous shapes such as placement-level contexts:,
section_path:, and string shorthand in display-only positions.
After schema validation, the generator checks:
section_idexists insections.yaml;- icon IDs exist in
icons.yaml; - literal owned contexts exist in the sibling metadata;
- literal widget references exist in metadata unless they carry the
explicit
unresolvedescape hatch; - dynamic selectors are declared by metadata guardrails;
- display widgets reference contexts but do not own them;
- every literal widget reference is owned somewhere else unless it is deliberately unresolved.
The matching and semantic validation start in
integrations/gen_taxonomy.py:745-790. Selector and literal-reference
validation live around integrations/gen_taxonomy.py:438-526.
Ownership model
The generated artifact separates ownership from display references:
- Structural strings and
type: owned_contextown literal contexts. - Structural
type: selectorowns the contexts matched bycontext_prefixorcollect_plugin. - Containers such as
group,flatten,grid,first_available, andview_switchaggregate context snapshots from their children. type: contextdisplay widgets reference contexts throughcontexts:but do not own them.
Generated items and placements therefore carry:
resolved_contexts: contexts owned by that node after child and selector aggregation.referenced_contexts: contexts referenced by display widgets.unresolved_references: staged widget references that intentionally do not resolve yet, withreason,owner,expires, anditem_path.
The recursive emission logic is in integrations/gen_taxonomy.py:551-719.
The FE-facing meaning of the generated fields is documented in
../in-app-contract.md.
Output artifact
The generated artifact is integrations/taxonomy.json. It is validated
against integrations/schemas/taxonomy_output.json and is intentionally
gitignored.
Top-level shape:
{
"taxonomy_schema_version": 1,
"source": {},
"sections": [],
"placements": [],
"opted_out_collectors": []
}
Important output concepts:
sections[]is the resolved global section registry.placements[]is the ordered list of collector-owned TOC placements.placements[].items[]is the normalized recursive item tree.collector_idslinks a placement back to the integration IDs produced from metadata.section_idis the stable registry handle;section_pathis the resolved path for consumers.
Assembly, deterministic placement sorting, and output schema validation
are handled in integrations/gen_taxonomy.py:847-883. Writing is handled
by the generator CLI in integrations/gen_taxonomy.py:890-920.
CI flow
Pull requests run the taxonomy checker from
.github/workflows/check-markdown.yml. The checker:
- validates all committed taxonomy sources by building the artifact;
- enforces taxonomy coverage when a PR changes a collector
taxonomy.yaml, adds/removes it, or edits metric-bearing parts ofmetadata.yaml; - runs the taxonomy unit tests.
See .github/workflows/check-markdown.yml:45-58 and
integrations/check_collector_taxonomy.py.
The master regeneration workflow runs integrations/gen_taxonomy.py as
part of the integrations regeneration job; see
.github/workflows/generate-integrations.yml:59-68. The generated
taxonomy.json is still a runtime/downstream contract artifact, not a
committed source file.
Worked mental model
For a static collector such as MySQL:
metadata.yamldeclaresmysql.queries.mysql/taxonomy.yamlownsmysql.queriesin a structural item.- A summary grid widget may also reference
mysql.queries. - The generated placement includes
mysql.queriesinresolved_contextsbecause it is owned, and inreferenced_contextswhere the widget uses it.
For a dynamic collector such as SNMP:
metadata.yamldeclares a dynamic namespace such assnmp..snmp/taxonomy.yamlmay use a narrower selector likesnmp.device_prof_under that declared namespace.- Selector items own the matched context snapshot; selector references inside widgets reference dynamic contexts without claiming ownership.
- The generated JSON preserves selector objects so downstream frontend code can resolve runtime dynamic contexts cleanly.
How I figured this out
Files read:
integrations/gen_taxonomy.pyintegrations/check_collector_taxonomy.pyintegrations/schemas/taxonomy_collector.jsonintegrations/schemas/taxonomy_output.json.github/workflows/check-markdown.yml.github/workflows/generate-integrations.yml../schema-reference.md../in-app-contract.md
Commands used during the original analysis:
rg -n "def module_contexts|def build_metadata_indexes|def process_taxonomy_file|def emit_item|def build_taxonomy" integrations/gen_taxonomy.py
rg -n "gen_taxonomy|check_collector_taxonomy|taxonomy.json|taxonomy.yaml" .github/workflows integrations/README.md .agents/sow/specs/taxonomy.md