Skip to content

feat(go.d/chartengine): support named active chart template sets - #23971

Merged
ilyam8 merged 7 commits into
netdata:masterfrom
ilyam8:codex/statsd-active-template-set
Sep 20, 2026
Merged

ilyam8 merged 7 commits into
netdata:masterfrom
ilyam8:codex/statsd-active-template-set

Conversation

@ilyam8

@ilyam8 ilyam8 commented Sep 20, 2026

Copy link
Copy Markdown
Member
Summary

Allow V2 collectors to add, remove, and replace named native chart templates at runtime while preserving unchanged entries’ lifecycle state. Adds transactional publication per host scope and native chart-coverage support as a prerequisite for Go StatsD. Existing YAML collectors remain supported through a separate static-provider interface.

Validated with framework tests, race checks, existing collector builds, Prometheus tooling and benchmarks.

Test Plan
Additional Information
For users: How does this change affect me?

Summary by cubic

Adds named active chart template sets to the chart engine, letting V2 collectors add, remove, and replace native chart entries at runtime without disturbing lifecycle state for unchanged entries. Changes are prepared as immutable snapshots and published transactionally per host scope, with static YAML collectors still supported through a separate provider; this is groundwork for Go StatsD collection.

New Features

  • Collectors can implement ChartTemplateSetProvider to expose a complete native template set; the getter is captured after Check and once after each successful Collect, and invalid candidates abort the staged metric cycle.
  • collecttest.AssertChartCoverage now validates native template sets alongside static YAML, aligning coverage with instance eligibility and excluding collision losers; pin intended winners with RequiredContexts.

Migration

  • CollectorV2 no longer declares ChartTemplateYAML(); metric jobs must implement exactly one of StaticChartTemplateProvider or ChartTemplateSetProvider.
  • Integer chart dimensions now emit type=int explicitly, so golden output and protocol assertions need updating.

Written for commit ad14df3. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Collectors can now provide either static chart definitions or native chart-template sets.
    • Chart-template sets support validated membership changes, policy overrides, and stable chart identity.
    • Template updates preserve existing chart state where possible and cleanly remove retired charts and dimensions.
    • Chart coverage supports native template sets, host scopes, and policy-based filtering.
    • Diagnostics now provide clearer chart-template and routing context.
  • Bug Fixes

    • Integer dimensions now explicitly report their type in emitted data.
  • Documentation

    • Updated collector, chart-template, migration, and runtime guidance for the new provider model.

@coderabbitai

This comment was marked as resolved.

@qodo-free-for-open-source-projects

This comment was marked as resolved.

@qodo-code-review

qodo-code-review Bot commented Sep 20, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (7)

Grey Divider


Action required

1. Startup claim omits its enforcer 📜 Skill insight ≡ Correctness
Description
how-to-write-a-collector.md says that implementing both providers is a startup error without
naming collectorapi.NewChartTemplateSource as the enforcing API. When readers need to verify or
update this startup behavior, the statement does not identify the implementation whose validation
result guarantees it.
Code

src/go/plugin/go.d/docs/how-to-write-a-collector.md[146]

+their getter; implementing both providers is a startup error. `collecttest.AssertChartCoverage` supports either provider.
Relevance

●●● Strong

Names the concrete startup validator, improving a deterministic enforcement claim in documentation.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3350844 requires an enforcement claim to identify the enforcing mechanism, operating mode, and
error or warning result. The guide identifies startup and an error, but the implementation shows
that the unnamed enforcement mechanism is collectorapi.NewChartTemplateSource, called during V2
job initialization.

src/go/plugin/go.d/docs/how-to-write-a-collector.md[144-146]
src/go/plugin/framework/collectorapi/chart_templates.go[23-31]
src/go/plugin/framework/jobruntime/job_v2.go[540-544]
Skill: repo-skill-authoring: Skill: repo-skill-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The documentation claims that implementing both chart providers causes a startup error but does not identify the API that enforces the check.
## Fix Focus Areas
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[144-146]
## Recommended Fix
State that `collectorapi.NewChartTemplateSource`, during V2 job startup validation, returns an error when a metric collector implements both chart-provider interfaces.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Startup claim omits its enforcer 📜 Skill insight ≡ Correctness
Description
how-to-write-a-collector.md says that implementing both providers is a startup error without
naming collectorapi.NewChartTemplateSource as the enforcing API. When readers need to verify or
update this startup behavior, the statement does not identify the implementation whose validation
result guarantees it.
Code

src/go/plugin/go.d/docs/how-to-write-a-collector.md[146]

+their getter; implementing both providers is a startup error. `collecttest.AssertChartCoverage` supports either provider.
Relevance

●●● Strong

Names the concrete startup validator, improving a deterministic enforcement claim in documentation.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3350844 requires an enforcement claim to identify the enforcing mechanism, operating mode, and
error or warning result. The guide identifies startup and an error, but the implementation shows
that the unnamed enforcement mechanism is collectorapi.NewChartTemplateSource, called during V2
job initialization.

src/go/plugin/go.d/docs/how-to-write-a-collector.md[144-146]
src/go/plugin/framework/collectorapi/chart_templates.go[23-31]
src/go/plugin/framework/jobruntime/job_v2.go[540-544]
Skill: repo-skill-authoring: Skill: repo-skill-authoring: Skill: repo-skill-authoring: Skill: repo-skill-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The documentation claims that implementing both chart providers causes a startup error but does not identify the API that enforces the check.
## Fix Focus Areas
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[144-146]
## Recommended Fix
State that `collectorapi.NewChartTemplateSource`, during V2 job startup validation, returns an error when a metric collector implements both chart-provider interfaces.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Old-host charts remain after a move 📜 Skill insight ≡ Correctness
Description
commitSuccessfulEmission clears cleanupCharts whenever a nonempty plan is admitted on a host
different from cleanupOwner, then releases the old owner without emitting removals. After a vnode
switch, the eventual cleanup snapshot contains only the new host's inventory, so charts already
published on the previous host cannot reach the obsolete and removed lifecycle.
Code

src/go/plugin/framework/jobruntime/job_v2_host_state.go[R113-114]

+	if s.cleanupOwner != decision.targetHost {
+		clear(s.cleanupCharts)
Evidence
Rule 3430195 requires a source that disappears to obsolete its chart and reach the removed
lifecycle. The changed host-state code clears the only old-host chart inventory, while owner release
merely unregisters ownership and final cleanup builds removals solely from the remaining cleanup
snapshot.

src/go/plugin/framework/jobruntime/job_v2_host_state.go[98-118]
src/go/plugin/framework/jobruntime/job_v2_cleanup.go[22-37]
src/go/plugin/framework/hostoutput/publisher.go[130-148]
src/go/plugin/framework/jobruntime/job_v2.go[284-312]
Skill: health-alert-authoring: Skill: health-alert-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Switching publication hosts clears the previous host's chart inventory without emitting obsolete actions for those charts, leaving them permanently published on the old host.
## Fix Focus Areas
- src/go/plugin/framework/jobruntime/job_v2_host_state.go[98-118]
- src/go/plugin/framework/jobruntime/job_v2_cleanup.go[22-37]
## Recommended Fix
Retain cleanup inventory separately for every host that accepted chart output. When publication moves to another host, preserve the old host's owner, definition, and charts until obsolete actions are successfully emitted to that host; add coverage for direct and empty-plan host switches followed by shutdown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (1)
4. Route diagnostics crash named collectors 🐞 Bug ☼ Reliability
Description
TemplateSet.diagnosticObserver unconditionally dereferences lookups for ChartTemplateID and
ExistingChartTemplateID, even when a diagnostic does not identify one of those charts. With a
route diagnostic observer enabled, ordinary resolved routes have no existing template ID and
series-filtered diagnostics have no chart template ID, so planning a named template set panics
instead of producing a plan.
Code

src/go/plugin/framework/chartengine/diagnostics.go[R183-186]

+		chart := s.index.chartsByID[fact.ChartTemplateID]
+		fact.TemplateEntryID, fact.LocalChartTemplateID = chart.EntryID, chart.LocalTemplateID
+		prior := s.index.chartsByID[fact.ExistingChartTemplateID]
+		fact.ExistingTemplateEntryID, fact.ExistingLocalChartTemplateID = prior.EntryID, prior.LocalTemplateID
Evidence
The new wrapper is installed for every named-set plan with an observer. Normal resolved-route facts
populate ChartTemplateID but not ExistingChartTemplateID, while filtered-series facts omit both
IDs, so the map lookups in the added wrapper can return nil before their fields are accessed.

src/go/plugin/framework/chartengine/diagnostics.go[178-205]
src/go/plugin/framework/chartengine/planner.go[427-431]
src/go/plugin/framework/chartengine/matcher.go[192-205]
src/go/plugin/framework/chartengine/planner.go[471-493]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`TemplateSet.diagnosticObserver` dereferences chart-index lookups even when the incoming diagnostic leaves `ChartTemplateID` or `ExistingChartTemplateID` empty. Route diagnostics commonly omit the existing chart, and filtered-series diagnostics omit both, so named template planning panics whenever a diagnostic observer is configured.
## Fix Focus Areas
- src/go/plugin/framework/chartengine/diagnostics.go[182-186]
## Recommended Fix
Check whether each template ID is non-empty and whether its lookup succeeds before reading provenance fields. Populate the corresponding provenance fields only for charts that exist, leaving them empty otherwise; add coverage for a resolved route without an existing owner and a filtered-series diagnostic.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Coverage tests reject valid chart sets ✓ Resolved 🐞 Bug ≡ Correctness
Description
expectedTemplateCoverage unions dimensions from every native entry with the same context without
accounting for rendered chart ownership. When entries intentionally share a public chart ID,
chartengine keeps the first route and rejects the other entry's dimensions, so AssertChartCoverage
requires dimensions that can never be materialized.
Code

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[R220-223]

+	for _, entry := range set.Entries() {
+		rootContext := normalizeOptionalContextPart(entry.ContextNamespace)
+		for _, group := range entry.Groups {
+			if err := collectTemplateContexts(byContextSet, group, rootContext, reader, contextMatchers, selectorParseCache, globalSelector); err != nil {
Relevance

●●● Strong

Expected coverage must follow planner ownership; otherwise valid overlapping native entries produce
false failures.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new native traversal collects every entry into one context-level dimension set. Native template
sets explicitly permit overlapping public IDs, while the planner rejects a route when another
template already owns that rendered chart ID; therefore the expected union diverges from the emitted
plan.

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[205-227]
src/go/plugin/framework/chartengine/template_set_test.go[60-79]
src/go/plugin/framework/chartengine/planner.go[621-674]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
Issue description
`expectedTemplateCoverage` treats all native entries matching a context as additive, but chartengine resolves collisions by public chart ID and admits only the winning template's routes. Make expected coverage use the same ownership result so supported overlapping native entries do not create impossible expected dimensions.
Fix Focus Areas
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[205-227]
- src/go/plugin/framework/chartengine/planner.go[621-674]
Recommended Fix
Track rendered public-chart ownership while deriving native expected coverage, using chartengine's precedence order, and discard dimensions from losing template entries. Add a native-set coverage test with two entries targeting the same public chart ID and distinct dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Coverage fails on missing labels ✓ Resolved 🐞 Bug ≡ Correctness
Description
collectTemplateContexts calls collectExpectedDimensionNames without applying each chart's
required instance-label policy. When a selector matches a series lacking an instances.by_labels
key, expected coverage includes its dimension although the planner rejects the route before
materialization.
Code

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[264]

+			dimNames, matched, err := collectExpectedDimensionNames(reader, dim, selectorParseCache, globalSelector)
Relevance

●●● Strong

Expected coverage must apply required instance-label policy to match planner materialization
behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new native-set coverage path walks every entry and chart, but its expected-dimension helper
checks only collection freshness, the global selector, and the dimension selector. The actual
planner resolves chart identity after selector matching, and its identity resolver returns false
whenever an explicit instance key is absent, so expected and actual coverage diverge.

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[220-226]
src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[253-278]
src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[322-341]
src/go/plugin/framework/chartengine/matcher.go[127-149]
src/go/plugin/framework/chartengine/identity.go[188-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Native chart coverage counts selector-matching series even when they cannot materialize the chart because required instance labels are missing.
## Fix Focus Areas
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[245-278]
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[307-341]
- src/go/plugin/framework/chartengine/identity.go[188-201]
## Recommended Fix
Pass each chart's instance-label configuration into expected-dimension collection and exclude series that do not satisfy all required instance labels, while preserving optional-label behavior. Add a coverage test containing a chart with a required instance label and a selector-matching series that omits it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Chart provider rule has many owners 📜 Skill insight ⌂ Architecture
Description
SKILL.md restates the requirement that V2 collectors expose exactly one chart provider instead of
only routing readers to the framework owner. The same fact also appears in the framework README and
two changed guidance files, so later provider changes must be synchronized across competing copies.
Code

.agents/skills/collectors-go-framework-v2/SKILL.md[R59-62]

+  `Collect()` and expose exactly one chart provider. Embedded `charts.yaml` through `ChartTemplateYAML()` is
+  RECOMMENDED for static definitions. Changing native membership uses `ChartTemplateSet()`; its ownership,
+  normalization, fixed-policy and replacement contracts are in
+  `src/go/plugin/framework/chartengine/README.md#named-active-template-sets`.
Relevance

●●● Strong

Accepted precedent favors replacing repeated guidance with authoritative-owner pointers.

PR-#23946

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3350823 requires each factual requirement to have one authoritative owner. The changed skill,
Agent guidance, and collector guide repeat the same exactly-one-provider requirement that is already
stated in the chartengine README.

.agents/skills/collectors-go-framework-v2/SKILL.md[59-62]
src/go/AGENTS.md[44-45]
src/go/plugin/go.d/docs/how-to-write-a-collector.md[120-121]
src/go/plugin/framework/chartengine/README.md[21-27]
Skill: repo-skill-authoring: Skill: repo-skill-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The requirement to expose exactly one chart provider is maintained in several documents instead of being owned by the chartengine framework documentation and referenced elsewhere.
## Fix Focus Areas
- .agents/skills/collectors-go-framework-v2/SKILL.md[59-62]
- src/go/AGENTS.md[44-45]
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[120-121]
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[142-146]
## Recommended Fix
Keep the complete provider contract in the chartengine README and replace the duplicated requirements in the skill and guidance documents with short subject pointers to that authoritative section.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (5)
8. Chart provider rule has many owners 📜 Skill insight ⌂ Architecture
Description
SKILL.md restates the requirement that V2 collectors expose exactly one chart provider instead of
only routing readers to the framework owner. The same fact also appears in the framework README and
two changed guidance files, so later provider changes must be synchronized across competing copies.
Code

.agents/skills/collectors-go-framework-v2/SKILL.md[R59-62]

+  `Collect()` and expose exactly one chart provider. Embedded `charts.yaml` through `ChartTemplateYAML()` is
+  RECOMMENDED for static definitions. Changing native membership uses `ChartTemplateSet()`; its ownership,
+  normalization, fixed-policy and replacement contracts are in
+  `src/go/plugin/framework/chartengine/README.md#named-active-template-sets`.
Relevance

●●● Strong

Accepted precedent favors replacing repeated guidance with authoritative-owner pointers.

PR-#23946

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3350823 requires each factual requirement to have one authoritative owner. The changed skill,
Agent guidance, and collector guide repeat the same exactly-one-provider requirement that is already
stated in the chartengine README.

.agents/skills/collectors-go-framework-v2/SKILL.md[59-62]
src/go/AGENTS.md[44-45]
src/go/plugin/go.d/docs/how-to-write-a-collector.md[120-121]
src/go/plugin/framework/chartengine/README.md[21-27]
Skill: repo-skill-authoring: Skill: repo-skill-authoring: Skill: repo-skill-authoring: Skill: repo-skill-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The requirement to expose exactly one chart provider is maintained in several documents instead of being owned by the chartengine framework documentation and referenced elsewhere.
## Fix Focus Areas
- .agents/skills/collectors-go-framework-v2/SKILL.md[59-62]
- src/go/AGENTS.md[44-45]
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[120-121]
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[142-146]
## Recommended Fix
Keep the complete provider contract in the chartengine README and replace the duplicated requirements in the skill and guidance documents with short subject pointers to that authoritative section.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Coverage fails on missing labels ✓ Resolved 🐞 Bug ≡ Correctness
Description
collectTemplateContexts calls collectExpectedDimensionNames without applying each chart's
required instance-label policy. When a selector matches a series lacking an instances.by_labels
key, expected coverage includes its dimension although the planner rejects the route before
materialization.
Code

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[264]

+			dimNames, matched, err := collectExpectedDimensionNames(reader, dim, selectorParseCache, globalSelector)
Relevance

●●● Strong

Expected coverage must apply required instance-label policy to match planner materialization
behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new native-set coverage path walks every entry and chart, but its expected-dimension helper
checks only collection freshness, the global selector, and the dimension selector. The actual
planner resolves chart identity after selector matching, and its identity resolver returns false
whenever an explicit instance key is absent, so expected and actual coverage diverge.

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[220-226]
src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[253-278]
src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[322-341]
src/go/plugin/framework/chartengine/matcher.go[127-149]
src/go/plugin/framework/chartengine/identity.go[188-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Native chart coverage counts selector-matching series even when they cannot materialize the chart because required instance labels are missing.
## Fix Focus Areas
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[245-278]
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[307-341]
- src/go/plugin/framework/chartengine/identity.go[188-201]
## Recommended Fix
Pass each chart's instance-label configuration into expected-dimension collection and exclude series that do not satisfy all required instance labels, while preserving optional-label behavior. Add a coverage test containing a chart with a required instance label and a selector-matching series that omits it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Coverage tests reject valid chart sets ✓ Resolved 🐞 Bug ≡ Correctness
Description
expectedTemplateCoverage unions dimensions from every native entry with the same context without
accounting for rendered chart ownership. When entries intentionally share a public chart ID,
chartengine keeps the first route and rejects the other entry's dimensions, so AssertChartCoverage
requires dimensions that can never be materialized.
Code

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[R220-223]

+	for _, entry := range set.Entries() {
+		rootContext := normalizeOptionalContextPart(entry.ContextNamespace)
+		for _, group := range entry.Groups {
+			if err := collectTemplateContexts(byContextSet, group, rootContext, reader, contextMatchers, selectorParseCache, globalSelector); err != nil {
Relevance

●●● Strong

Expected coverage must follow planner ownership; otherwise valid overlapping native entries produce
false failures.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new native traversal collects every entry into one context-level dimension set. Native template
sets explicitly permit overlapping public IDs, while the planner rejects a route when another
template already owns that rendered chart ID; therefore the expected union diverges from the emitted
plan.

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[205-227]
src/go/plugin/framework/chartengine/template_set_test.go[60-79]
src/go/plugin/framework/chartengine/planner.go[621-674]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
Issue description
`expectedTemplateCoverage` treats all native entries matching a context as additive, but chartengine resolves collisions by public chart ID and admits only the winning template's routes. Make expected coverage use the same ownership result so supported overlapping native entries do not create impossible expected dimensions.
Fix Focus Areas
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[205-227]
- src/go/plugin/framework/chartengine/planner.go[621-674]
Recommended Fix
Track rendered public-chart ownership while deriving native expected coverage, using chartengine's precedence order, and discard dimensions from losing template entries. Add a native-set coverage test with two entries targeting the same public chart ID and distinct dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Repeated template failures flood logs 📜 Skill insight ◔ Observability
Description
collectAndEmit calls Warningf directly when ChartTemplateSource.Capture() fails, without using
the logger's limiter. A provider that repeatedly returns a nil or invalid template reaches this path
every collection cycle, so the same recoverable warning is emitted without bound.
Code

src/go/plugin/framework/jobruntime/job_v2.go[642]

+		j.Warningf("chart template capture failed: %v", sanitizeLifecycleError(j.lifecycleErrorSanitizer, err))
Evidence
Rule 3350612 requires recoverable per-cycle warnings to use the framework limiter with a stable,
low-cardinality key. The changed collection path retries template capture each cycle and logs every
failure directly with Warningf.

src/go/plugin/framework/jobruntime/job_v2.go[638-643]
src/go/plugin/framework/collectorapi/chart_templates.go[43-74]
Skill: collectors-authoring: Skill: collectors-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Template capture failures can recur on every collection cycle, but the warning bypasses the framework logger limiter and can flood logs.
## Fix Focus Areas
- src/go/plugin/framework/jobruntime/job_v2.go[638-643]
## Recommended Fix
Emit the warning through the framework logger's `Limit` mechanism using a stable operation-level key and an appropriate recurrence interval. Keep provider values and error text out of the limiter key.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. Template logs omit expected results 📜 Skill insight ◔ Observability
Description
collectAndEmit logs chart template capture failed: %v, which names the operation but does not
state the expected valid snapshot or the observed capture result. When a provider supplies a nil
template, an invalid template, or changed fixed policy, all cases flow through this message and
operators must infer the violated contract from varying cause text.
Code

src/go/plugin/framework/jobruntime/job_v2.go[642]

+		j.Warningf("chart template capture failed: %v", sanitizeLifecycleError(j.lifecycleErrorSanitizer, err))
Evidence
Rule 3350573 requires new error logs to include the operation, target, expected result, and observed
result. The added warning identifies template capture as the operation but supplies no explicit
target or expected-versus-observed details.

src/go/plugin/framework/jobruntime/job_v2.go[638-643]
Skill: collectors-authoring: Skill: collectors-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new template-capture warning does not explicitly identify the target or distinguish the expected valid, stable template snapshot from the observed failure.
## Fix Focus Areas
- src/go/plugin/framework/jobruntime/job_v2.go[638-643]
## Recommended Fix
Rewrite the warning to identify the affected job, state that a valid template snapshot with stable policy was expected, and describe the observed sanitized capture error.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .agents/skills/collectors-go-framework-v2/SKILL.md
Comment thread src/go/plugin/go.d/docs/how-to-write-a-collector.md
Comment thread src/go/plugin/go.d/pkg/collecttest/chart_coverage.go Outdated
Comment thread src/go/plugin/go.d/pkg/collecttest/chart_coverage.go Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (5)

Grey Divider


Action required

1. Old-host charts remain after a move 📜 Skill insight ≡ Correctness ⭐ New
Description
commitSuccessfulEmission clears cleanupCharts whenever a nonempty plan is admitted on a host
different from cleanupOwner, then releases the old owner without emitting removals. After a vnode
switch, the eventual cleanup snapshot contains only the new host's inventory, so charts already
published on the previous host cannot reach the obsolete and removed lifecycle.
Code

src/go/plugin/framework/jobruntime/job_v2_host_state.go[R113-114]

+	if s.cleanupOwner != decision.targetHost {
+		clear(s.cleanupCharts)
Evidence
Rule 3430195 requires a source that disappears to obsolete its chart and reach the removed
lifecycle. The changed host-state code clears the only old-host chart inventory, while owner release
merely unregisters ownership and final cleanup builds removals solely from the remaining cleanup
snapshot.

src/go/plugin/framework/jobruntime/job_v2_host_state.go[98-118]
src/go/plugin/framework/jobruntime/job_v2_cleanup.go[22-37]
src/go/plugin/framework/hostoutput/publisher.go[130-148]
src/go/plugin/framework/jobruntime/job_v2.go[284-312]
Skill: health-alert-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Switching publication hosts clears the previous host's chart inventory without emitting obsolete actions for those charts, leaving them permanently published on the old host.

## Fix Focus Areas
- src/go/plugin/framework/jobruntime/job_v2_host_state.go[98-118]
- src/go/plugin/framework/jobruntime/job_v2_cleanup.go[22-37]

## Recommended Fix
Retain cleanup inventory separately for every host that accepted chart output. When publication moves to another host, preserve the old host's owner, definition, and charts until obsolete actions are successfully emitted to that host; add coverage for direct and empty-plan host switches followed by shutdown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Route diagnostics crash named collectors 🐞 Bug ☼ Reliability ⭐ New
Description
TemplateSet.diagnosticObserver unconditionally dereferences lookups for ChartTemplateID and
ExistingChartTemplateID, even when a diagnostic does not identify one of those charts. With a
route diagnostic observer enabled, ordinary resolved routes have no existing template ID and
series-filtered diagnostics have no chart template ID, so planning a named template set panics
instead of producing a plan.
Code

src/go/plugin/framework/chartengine/diagnostics.go[R183-186]

+		chart := s.index.chartsByID[fact.ChartTemplateID]
+		fact.TemplateEntryID, fact.LocalChartTemplateID = chart.EntryID, chart.LocalTemplateID
+		prior := s.index.chartsByID[fact.ExistingChartTemplateID]
+		fact.ExistingTemplateEntryID, fact.ExistingLocalChartTemplateID = prior.EntryID, prior.LocalTemplateID
Evidence
The new wrapper is installed for every named-set plan with an observer. Normal resolved-route facts
populate ChartTemplateID but not ExistingChartTemplateID, while filtered-series facts omit both
IDs, so the map lookups in the added wrapper can return nil before their fields are accessed.

src/go/plugin/framework/chartengine/diagnostics.go[178-205]
src/go/plugin/framework/chartengine/planner.go[427-431]
src/go/plugin/framework/chartengine/matcher.go[192-205]
src/go/plugin/framework/chartengine/planner.go[471-493]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`TemplateSet.diagnosticObserver` dereferences chart-index lookups even when the incoming diagnostic leaves `ChartTemplateID` or `ExistingChartTemplateID` empty. Route diagnostics commonly omit the existing chart, and filtered-series diagnostics omit both, so named template planning panics whenever a diagnostic observer is configured.

## Fix Focus Areas
- src/go/plugin/framework/chartengine/diagnostics.go[182-186]

## Recommended Fix
Check whether each template ID is non-empty and whether its lookup succeeds before reading provenance fields. Populate the corresponding provenance fields only for charts that exist, leaving them empty otherwise; add coverage for a resolved route without an existing owner and a filtered-series diagnostic.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Startup claim omits its enforcer 📜 Skill insight ≡ Correctness
Description
how-to-write-a-collector.md says that implementing both providers is a startup error without
naming collectorapi.NewChartTemplateSource as the enforcing API. When readers need to verify or
update this startup behavior, the statement does not identify the implementation whose validation
result guarantees it.
Code

src/go/plugin/go.d/docs/how-to-write-a-collector.md[146]

+their getter; implementing both providers is a startup error. `collecttest.AssertChartCoverage` supports either provider.
Relevance

●●● Strong

Names the concrete startup validator, improving a deterministic enforcement claim in documentation.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3350844 requires an enforcement claim to identify the enforcing mechanism, operating mode, and
error or warning result. The guide identifies startup and an error, but the implementation shows
that the unnamed enforcement mechanism is collectorapi.NewChartTemplateSource, called during V2
job initialization.

src/go/plugin/go.d/docs/how-to-write-a-collector.md[144-146]
src/go/plugin/framework/collectorapi/chart_templates.go[23-31]
src/go/plugin/framework/jobruntime/job_v2.go[540-544]
Skill: repo-skill-authoring: Skill: repo-skill-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The documentation claims that implementing both chart providers causes a startup error but does not identify the API that enforces the check.
## Fix Focus Areas
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[144-146]
## Recommended Fix
State that `collectorapi.NewChartTemplateSource`, during V2 job startup validation, returns an error when a metric collector implements both chart-provider interfaces.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Repeated template failures flood logs 📜 Skill insight ◔ Observability ⭐ New
Description
collectAndEmit calls Warningf directly when ChartTemplateSource.Capture() fails, without using
the logger's limiter. A provider that repeatedly returns a nil or invalid template reaches this path
every collection cycle, so the same recoverable warning is emitted without bound.
Code

src/go/plugin/framework/jobruntime/job_v2.go[642]

+		j.Warningf("chart template capture failed: %v", sanitizeLifecycleError(j.lifecycleErrorSanitizer, err))
Evidence
Rule 3350612 requires recoverable per-cycle warnings to use the framework limiter with a stable,
low-cardinality key. The changed collection path retries template capture each cycle and logs every
failure directly with Warningf.

src/go/plugin/framework/jobruntime/job_v2.go[638-643]
src/go/plugin/framework/collectorapi/chart_templates.go[43-74]
Skill: collectors-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Template capture failures can recur on every collection cycle, but the warning bypasses the framework logger limiter and can flood logs.

## Fix Focus Areas
- src/go/plugin/framework/jobruntime/job_v2.go[638-643]

## Recommended Fix
Emit the warning through the framework logger's `Limit` mechanism using a stable operation-level key and an appropriate recurrence interval. Keep provider values and error text out of the limiter key.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Template logs omit expected results 📜 Skill insight ◔ Observability ⭐ New
Description
collectAndEmit logs chart template capture failed: %v, which names the operation but does not
state the expected valid snapshot or the observed capture result. When a provider supplies a nil
template, an invalid template, or changed fixed policy, all cases flow through this message and
operators must infer the violated contract from varying cause text.
Code

src/go/plugin/framework/jobruntime/job_v2.go[642]

+		j.Warningf("chart template capture failed: %v", sanitizeLifecycleError(j.lifecycleErrorSanitizer, err))
Evidence
Rule 3350573 requires new error logs to include the operation, target, expected result, and observed
result. The added warning identifies template capture as the operation but supplies no explicit
target or expected-versus-observed details.

src/go/plugin/framework/jobruntime/job_v2.go[638-643]
Skill: collectors-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new template-capture warning does not explicitly identify the target or distinguish the expected valid, stable template snapshot from the observed failure.

## Fix Focus Areas
- src/go/plugin/framework/jobruntime/job_v2.go[638-643]

## Recommended Fix
Rewrite the warning to identify the affected job, state that a valid template snapshot with stable policy was expected, and describe the observed sanitized capture error.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Chart provider rule has many owners 📜 Skill insight ⌂ Architecture
Description
SKILL.md restates the requirement that V2 collectors expose exactly one chart provider instead of
only routing readers to the framework owner. The same fact also appears in the framework README and
two changed guidance files, so later provider changes must be synchronized across competing copies.
Code

.agents/skills/collectors-go-framework-v2/SKILL.md[R59-62]

+  `Collect()` and expose exactly one chart provider. Embedded `charts.yaml` through `ChartTemplateYAML()` is
+  RECOMMENDED for static definitions. Changing native membership uses `ChartTemplateSet()`; its ownership,
+  normalization, fixed-policy and replacement contracts are in
+  `src/go/plugin/framework/chartengine/README.md#named-active-template-sets`.
Relevance

●●● Strong

Accepted precedent favors replacing repeated guidance with authoritative-owner pointers.

PR-#23946

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Rule 3350823 requires each factual requirement to have one authoritative owner. The changed skill,
Agent guidance, and collector guide repeat the same exactly-one-provider requirement that is already
stated in the chartengine README.

.agents/skills/collectors-go-framework-v2/SKILL.md[59-62]
src/go/AGENTS.md[44-45]
src/go/plugin/go.d/docs/how-to-write-a-collector.md[120-121]
src/go/plugin/framework/chartengine/README.md[21-27]
Skill: repo-skill-authoring: Skill: repo-skill-authoring

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The requirement to expose exactly one chart provider is maintained in several documents instead of being owned by the chartengine framework documentation and referenced elsewhere.
## Fix Focus Areas
- .agents/skills/collectors-go-framework-v2/SKILL.md[59-62]
- src/go/AGENTS.md[44-45]
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[120-121]
- src/go/plugin/go.d/docs/how-to-write-a-collector.md[142-146]
## Recommended Fix
Keep the complete provider contract in the chartengine README and replace the duplicated requirements in the skill and guidance documents with short subject pointers to that authoritative section.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View medium (2)
7. Coverage fails on missing labels 🐞 Bug ≡ Correctness
Description
collectTemplateContexts calls collectExpectedDimensionNames without applying each chart's
required instance-label policy. When a selector matches a series lacking an instances.by_labels
key, expected coverage includes its dimension although the planner rejects the route before
materialization.
Code

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[264]

+			dimNames, matched, err := collectExpectedDimensionNames(reader, dim, selectorParseCache, globalSelector)
Relevance

●●● Strong

Expected coverage must apply required instance-label policy to match planner materialization
behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new native-set coverage path walks every entry and chart, but its expected-dimension helper
checks only collection freshness, the global selector, and the dimension selector. The actual
planner resolves chart identity after selector matching, and its identity resolver returns false
whenever an explicit instance key is absent, so expected and actual coverage diverge.

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[220-226]
src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[253-278]
src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[322-341]
src/go/plugin/framework/chartengine/matcher.go[127-149]
src/go/plugin/framework/chartengine/identity.go[188-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Native chart coverage counts selector-matching series even when they cannot materialize the chart because required instance labels are missing.
## Fix Focus Areas
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[245-278]
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[307-341]
- src/go/plugin/framework/chartengine/identity.go[188-201]
## Recommended Fix
Pass each chart's instance-label configuration into expected-dimension collection and exclude series that do not satisfy all required instance labels, while preserving optional-label behavior. Add a coverage test containing a chart with a required instance label and a selector-matching series that omits it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Coverage tests reject valid chart sets 🐞 Bug ≡ Correctness
Description
expectedTemplateCoverage unions dimensions from every native entry with the same context without
accounting for rendered chart ownership. When entries intentionally share a public chart ID,
chartengine keeps the first route and rejects the other entry's dimensions, so AssertChartCoverage
requires dimensions that can never be materialized.
Code

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[R220-223]

+	for _, entry := range set.Entries() {
+		rootContext := normalizeOptionalContextPart(entry.ContextNamespace)
+		for _, group := range entry.Groups {
+			if err := collectTemplateContexts(byContextSet, group, rootContext, reader, contextMatchers, selectorParseCache, globalSelector); err != nil {
Relevance

●●● Strong

Expected coverage must follow planner ownership; otherwise valid overlapping native entries produce
false failures.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new native traversal collects every entry into one context-level dimension set. Native template
sets explicitly permit overlapping public IDs, while the planner rejects a route when another
template already owns that rendered chart ID; therefore the expected union diverges from the emitted
plan.

src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[205-227]
src/go/plugin/framework/chartengine/template_set_test.go[60-79]
src/go/plugin/framework/chartengine/planner.go[621-674]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
Issue description
`expectedTemplateCoverage` treats all native entries matching a context as additive, but chartengine resolves collisions by public chart ID and admits only the winning template's routes. Make expected coverage use the same ownership result so supported overlapping native entries do not create impossible expected dimensions.
Fix Focus Areas
- src/go/plugin/go.d/pkg/collecttest/chart_coverage.go[205-227]
- src/go/plugin/framework/chartengine/planner.go[621-674]
Recommended Fix
Track rendered public-chart ownership while deriving native expected coverage, using chartengine's precedence order, and discard dimensions from losing template entries. Add a native-set coverage test with two entries targeting the same public chart ID and distinct dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 174 rules
✅ Skills: 8 invoked
  collectors-authoring
  repo-skill-authoring
  collectors-go-framework-v2
  collectors-metadata-yaml
  triage-support-bundle
  integrations-lifecycle
  collectors-go-design
  health-alert-authoring
✅ Cross-repo context — repo relationships
Review mode: 🧠 Deep: This is a broad, high-risk runtime framework change with substantial new transactional, lifecycle, policy, concurrency, and multi-host logic across many independent code paths, making redundant review materially valuable.

Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/go/plugin/framework/jobruntime/job_v2.go
Comment thread src/go/plugin/framework/jobruntime/job_v2.go
Comment thread src/go/plugin/framework/jobruntime/job_v2_host_state.go
Comment thread src/go/plugin/framework/chartengine/diagnostics.go

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 39 files

You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Architecture diagram
sequenceDiagram
    participant C as Collector (V2)
    participant CTS as ChartTemplateSource
    participant TS as TemplateSet (Immutable)
    participant CTE as ChartTemplateEngine
    participant SM as ScopeManager
    participant HS as HostScope State
    participant OE as Output Emitter

    Note over C,OE: NEW: Named Active Template Set Publication Flow

    C->>CTS: ChartTemplateSet() (new/changed set)
    activate CTS
    CTS->>CTS: Validate provider + no fixed policy change
    CTS->>TS: PrepareTemplateSet (apply job overrides)
    CTS-->>C: Prepared TemplateSet (stable pointer)
    deactivate CTS

    Note over C,SM: Captured per scope after successful collect

    C->>SM: Prepare emission per scope
    SM->>HS: Capture previous committed state
    HS-->>SM: Retained charts/dimensions
    SM->>CTE: PreparePlanWithOptions(reader, TemplateSet, ResetMaterialized)
    activate CTE
    CTE->>CTE: compare entries (preserved vs changed)
    alt unchanged entries
        CTE->>HS: preserve lifecycle state
    else changed/replaced entries
        CTE->>CTE: release old reservations
        CTE->>HS: recreate/retire affected charts
    else new host reset
        CTE->>CTE: stage fresh materialized state
    end
    CTE-->>SM: PlanAttempt (staged, not committed)
    deactivate CTE

    SM->>OE: Commit output (per scope)
    alt output accepted
        OE-->>SM: Commit success
        SM->>CTE: Commit (install program + route cache)
        CTE-->>HS: Update committed state + lifecycle
    else output rejected
        OE-->>SM: Abort
        Note over SM,HS: Scope keeps previous presentation
    end
Loading

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread src/go/plugin/framework/chartengine/matcher.go
Comment thread src/go/plugin/framework/chartengine/planner.go
Comment thread src/go/plugin/framework/collectorapi/collector.go Outdated
Comment thread src/go/plugin/framework/chartengine/template_set.go
Comment thread src/go/plugin/framework/chartengine/template_transition.go
Comment thread src/go/plugin/framework/chartengine/README.md
Comment thread src/go/plugin/go.d/pkg/collecttest/chart_set_test.go
Comment thread src/go/plugin/framework/charttpl/defaults.go
Comment thread src/go/plugin/framework/chartengine/compiler.go
Comment thread src/go/plugin/go.d/docs/how-to-write-a-collector.md

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 6 files (changes from recent commits).

You’re at about 96% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/go/plugin/framework/chartengine/README.md
@sonarqubecloud

Copy link
Copy Markdown

@ilyam8
ilyam8 merged commit b175f42 into netdata:master Sep 20, 2026
153 of 154 checks passed
@ilyam8
ilyam8 deleted the codex/statsd-active-template-set branch September 20, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants