Collector consistency rule
<repo>/AGENTS.md declares ("Collector Consistency
Requirements") that any change touching a collector MUST land
in one PR with matching changes to all relevant collector artifacts:
- The code -- the collector implementation files.
metadata.yaml-- the integration page driver.taxonomy.yaml-- dashboard table-of-contents placement for the collector's chart contexts.config_schema.json-- the dashboard's DYNCFG editor.- The stock
.conf-- what/etc/netdata/<plugin>/...ships. health.d/*.conf-- alert definitions for the collector's metrics.README.md-- comprehensive end-user documentation (often a symlink into the generatedintegrations/<slug>.md; seeartifacts-and-banners.md).
The old "5-file" shorthand is stale. Treat the list above as the durable review checklist; a given PR may legitimately not touch every file, but it must explain why an affected artifact does not need a matching edit.
Because most cross-artifact checks are not enforced by CI, collector PR descriptions MUST enumerate the relevant consistency artifacts and justify every artifact that did not need a matching change. Any SHOULD-level exception or escape hatch used by the implementation MUST be visible in the PR description or design note, not only in a code comment.
The rule covers obvious cases (units change in code -> update metadata.yaml; new config option -> update schema, stock conf, and docs; new metric -> update metadata.yaml, taxonomy.yaml, and README.md) and subtle ones (renaming a metric label affects the alert definition that refers to it; changing a default affects the stock conf example and the documented default value).
What enforces this rule today
Nothing automated, in most cases. Specifically:
gen_integrations.pyvalidates eachmetadata.yamlagainst its JSON Schema only. It does NOT cross-check againstconfig_schema.json, the stock.conf, orhealth.d/*.conf.gen_taxonomy.pyvalidates committed collectortaxonomy.yamlfiles, cross-references literal owned contexts and widget references againstmetadata.yaml, requires declared dynamic selectors, and emits the gitignoredintegrations/taxonomy.jsonartifact.check_collector_taxonomy.pyis wired intocheck-markdown.ymlfor pull requests. It fails when a PR touches a collectortaxonomy.yaml, adds/removes it, or edits ametadata.yamlmetrics block without a matchingtaxonomy.yaml. Non-metrics-onlymetadata.yamledits such as setup prose, overview text, categories, and troubleshooting do not trigger touched-collector taxonomy coverage by themselves, although the global taxonomy validation still runs.integrations/check_collector_metadata.pyis broken (seegotchas.mdfor details). Its imports refer to symbols that no longer exist ingen_integrations.py. ImportError on first run. NOT invoked from any workflow.- No CI workflow runs a "verify metric names in metadata.yaml exist in the collector code" check.
- No CI workflow runs a "verify
health.d/*.confalert metric names exist in the collector" check. check-markdown.ymlonly validates that generated markdown links resolve through Learn ingest -- not that metadata.yaml is in sync with config/schema/stock-conf/README.
The one exception is ibm.d modules: their metadata.yaml,
README.md, and config_schema.json are GENERATED by the
ibm.d docgen tool from contexts.yaml + config.go +
module.yaml, so those three files are consistent by
construction. ibm.d does NOT generate the stock .conf or
health.d/<...>.conf; those still need manual sync. See
ibm-d.md.
What reviewers should check
When reviewing a PR that touches a collector, verify:
Code changes have matching
metadata.yamlchanges. If the diff adds a chart, dimension, label, or unit change in the code, the corresponding entry must appear inmetadata.yaml. If a metric is renamed, both files must change.Chart-context changes have matching
taxonomy.yamlchanges. Structuralitems:entries that own contexts and widgetcontexts:references must name real contexts in the collector'smetadata.yaml. Dynamic contexts must usetype: selectoror selector objects withcontext_prefix:orcollect_plugin:and the correspondingmetrics.dynamic_context_prefixes:ormetrics.dynamic_collect_plugins:declaration.Config changes propagate to all four config-related files.
- The Go struct field (in
config.go). config_schema.json-- the field appears with the correct type, default, validation.- The stock
.conf-- a representative example shows the option. metadata.yaml-- the option appears undersetup.configuration.options.list.
- The Go struct field (in
Alert changes have matching
metadata.yaml.alertsentries. Ifhealth.d/<plugin>.confadds, removes, or renames an alert,metadata.yaml.modules.<m>.alerts[]must reflect the change.README.md handling. If the plugin directory has a single integration, the README is a symlink to the generated
integrations/<slug>.md-- the symlink target already updates whenmetadata.yamlupdates. If the plugin directory has multiple integrations, the README is hand-written and must be updated by the author.agent_notificationis a special case: the README itself is the generated artifact (nointegrations/subdir).integrations/<slug>.mdregenerated. The author should have run the pipeline locally and committed the updated.mdfile.check-markdown.ymlwill re-run the pipeline in CI; if the author's commit and CI's regen diverge, the PR fails.Umbrella pages. If the diff added or removed a collector,
src/collectors/COLLECTORS.mdshould reflect it. Same forSECRETS.md(secretstore changes) andSERVICE-DISCOVERY.md(service-discovery changes -- but note this one is NOT in CI; manual regen required).Generated artifacts are outputs, not source. Files with
DO NOT EDIT THIS FILE DIRECTLYor<!--startmeta ... message: "DO NOT EDIT..." -->banners must be regenerated from their source artifacts. Do not hand-edit generated files to fix prose, links, setup text, or metric descriptions.Gitignored generated catalogs are absent from the PR. Before opening the PR, run:
git status --porcelain |
rg '^(\?\?|!!| M|M |A |AM) integrations/(integrations\.(js|json)|taxonomy\.json)$' || true
The command MUST print no output. If it prints
integrations/integrations.js, integrations/integrations.json, or
integrations/taxonomy.json, remove the local generated artifact from the
commit/worktree state rather than committing it.
Why the policy is unenforced
Investigation of the repo found that
integrations/check_collector_metadata.py was apparently the
first stab at automation; it appears to have bit-rotted
without a CI hook to catch the rot. A proper enforcement
pipeline would:
- repair / rewrite
check_collector_metadata.py(or replace with a unit test undertests/); - wire it into
generate-integrations.ymlas a pre-flight validator; - add a metric-name cross-reference check between
metadata.yaml.alerts[].metricand the alert configurations underhealth.d/; - add a config-name cross-reference check between
metadata.yaml.setup.configuration.options.list[].nameandconfig_schema.jsonproperties.
Tracked as a followup SOW after integrations-lifecycle ships.
For now, the consistency rule is a review-time policy.
Anti-patterns to flag in review
- "I only changed the code; the docs can be a follow-up PR." -> No. Affected collector consistency artifacts move in one PR.
- "The integration page on Learn doesn't show my new option."
-> Author forgot to update
metadata.yamlAND regenerateintegrations/<slug>.md. - "I edited
integrations/<slug>.mddirectly to fix a description." -> No. That file is generated. Editmetadata.yamland regenerate. - "I edited
metadata.yamlfor an ibm.d module." -> No. Editcontexts.yaml,config.go, ormodule.yamland rungo generate. - "I changed a default in the stock
.confonly." -> Updateconfig_schema.jsondefault,metadata.yaml.setup.configuration.options.list[].default_value, and the README in lockstep. - "I added a chart context but skipped
taxonomy.yamlbecause the dashboard will discover it." -> No. Add the context to a placement or use a declared dynamic selector.