| 1 | --- |
| 2 | name: project-writing-go-modules-framework-v2 |
| 3 | description: Use when creating or migrating a Go go.d collector to framework V2, touching CollectorV2, metrix.CollectorStore, ChartTemplateYAML/charts.yaml, charttpl/chartengine, V2 host scopes/vnodes, or V2 collector tests. Focuses on concise maintainer-preferred V2 collector patterns. |
| 4 | --- |
| 5 | |
| 6 | # Writing Go go.d Modules With Framework V2 |
| 7 | |
| 8 | Use with `project-writing-collectors`. Keep this skill loaded for style; read |
| 9 | source files for evidence. |
| 10 | |
| 11 | ## Read First |
| 12 | |
| 13 | - Contract: `src/go/plugin/framework/collectorapi/collector.go` |
| 14 | - Framework-change workflow: |
| 15 | `src/go/plugin/framework/docs/changing-framework-code.md` |
| 16 | - Canonical new-collector guide: |
| 17 | `src/go/plugin/go.d/docs/how-to-write-a-collector.md` |
| 18 | - Helper-package guide: |
| 19 | `src/go/plugin/go.d/docs/helper-packages.md` |
| 20 | - V1-to-V2 migration guide: |
| 21 | `src/go/plugin/go.d/docs/migrate-v1-to-v2.md` |
| 22 | - Runtime/chart lifecycle: `src/go/plugin/framework/chartengine/README.md` |
| 23 | - Template format: `src/go/plugin/framework/charttpl/README.md` |
| 24 | - Host scopes/vnodes: `.agents/sow/specs/go-v2-host-scope.md` |
| 25 | - Primary modern example: `src/go/plugin/go.d/collector/cato_networks/`. |
| 26 | Use focused pieces from it, not the whole collector shape. |
| 27 | - Older V2 collectors can still be useful for local patterns, but review them |
| 28 | for stale style before treating them as examples. |
| 29 | |
| 30 | ## Decision Discipline |
| 31 | |
| 32 | - You MUST aim for the clean end state, not the smallest collector diff. If a |
| 33 | framework capability is missing and the problem is general, design the |
| 34 | framework change instead of hiding the issue in collector-local glue. |
| 35 | - If any framework-scope package changes, stop and satisfy |
| 36 | `src/go/plugin/framework/docs/changing-framework-code.md` before writing code. |
| 37 | - You MUST re-check scope after each coherent batch. If the work reveals an |
| 38 | independent collector cleanup, framework fix, or integration-doc change, |
| 39 | either defer it explicitly or land it separately before continuing. |
| 40 | |
| 41 | ## Core Style |
| 42 | |
| 43 | - New collectors MUST implement `collectorapi.CollectorV2` from |
| 44 | `src/go/plugin/framework/collectorapi/collector.go` and register via |
| 45 | `CreateV2`. |
| 46 | - `New()` SHOULD own defaults, `metrix.NewCollectorStore()`, typed metric |
| 47 | instruments, and test seams. |
| 48 | - V2 collectors MUST write metrics through `metrix.CollectorStore` during |
| 49 | `Collect()` and provide chart template YAML through `ChartTemplateYAML()`; |
| 50 | embedded `charts.yaml` is RECOMMENDED. |
| 51 | - `Collect(ctx)` MUST return `error` and write metrics to `metrix`; it MUST NOT |
| 52 | return a V1 `map[string]int64`. |
| 53 | - Files SHOULD stay boring: public lifecycle methods in `collector.go`, setup |
| 54 | helpers in `init.go` when needed, orchestration in `collect.go`, distinct |
| 55 | upstream operations in `collect_<operation>.go`, metrics in `metrix.go` / |
| 56 | `write_metrics.go`, focused tests. |
| 57 | - Before adding custom HTTP, selector, logging, command-execution, SQL, ping, |
| 58 | or log-file plumbing, check `src/go/plugin/go.d/docs/helper-packages.md` and |
| 59 | reuse an existing helper when it fits. |
| 60 | - If Functions exist, isolate them in a `<name>func/` subpackage with a narrow |
| 61 | `Deps` interface declared there. The Function package MUST NOT import the |
| 62 | collector package or hold `*Collector`. |
| 63 | - Public config options SHOULD stay small and justified. A proposed config |
| 64 | option MUST name the concrete operator decision it enables; "operators may |
| 65 | want to tune it" is not enough. Internal tuning SHOULD use constants unless |
| 66 | the operator has a real decision to make. |
| 67 | |
| 68 | ## Metrics And Charts |
| 69 | |
| 70 | - Instruments SHOULD be created once when the metric surface is known. |
| 71 | - Use `store.Write().SnapshotMeter("")` for normal metrics. |
| 72 | - Use `Vec(...)` for labels, `Gauge` for current values, |
| 73 | `Counter.ObserveTotal()` for source counters, and `StateSet` for fixed |
| 74 | one-active-state values. |
| 75 | - Metric names MUST be stable and selected by `charts.yaml`. |
| 76 | - In `charts.yaml`: use `version: v1`, `context_namespace`, `instances.by_labels`, |
| 77 | `label_promotion`, `algorithm: incremental` for counters, and `absolute` for |
| 78 | gauges. |
| 79 | - Put multipliers, divisors, hidden flags, and float formatting in the chart |
| 80 | template, not ad hoc chart-emission code. |
| 81 | - `metrix` registers a descriptor per metric NAME permanently (no unregister), so |
| 82 | re-registering a name with a changed kind, summary quantile set, or histogram |
| 83 | bounds PANICS. When a name's contract can drift across cycles, keep the per-name |
| 84 | handle for the job lifetime and SKIP a drifted series instead of re-registering. |
| 85 | - To reproduce a V1 chart context in a migration, inject `context_namespace` (the |
| 86 | fixed prefix, or `prefix.<app>` per job) so autogen rebuilds `prefix.<metric>` / |
| 87 | `prefix.<app>.<metric>` without hand-built chart IDs. |
| 88 | - Skip empty distributions -- e.g. a summary whose every quantile is NaN -- so a |
| 89 | chart waits for real data, matching how scalar NaN values are already skipped. |
| 90 | - For dynamic surfaces whose label sets churn, `metrix`'s `Vec` handle cache is |
| 91 | unbounded; cache per-series instruments yourself and evict handles unseen for N |
| 92 | cycles to stay bounded. Prefer a framework fix if the need is general |
| 93 | (Decision Discipline). |
| 94 | |
| 95 | ## Compatibility Rules |
| 96 | |
| 97 | - For V1-to-V2 migrations, start with |
| 98 | `src/go/plugin/go.d/docs/migrate-v1-to-v2.md`. |
| 99 | |
| 100 | ### Migration Hard Stops |
| 101 | |
| 102 | - A collector using V1 chart `Vars` is blocked until framework support, an |
| 103 | approved equivalent design, or explicit breaking-alert approval exists. |
| 104 | - `collecttest.AssertChartCoverage` is not chart-identity parity; it cannot |
| 105 | prove old chart IDs, family, priority, lifecycle, labels, or alert variables. |
| 106 | - A finished migration MUST pass an import/runtime-path audit proving no V1 |
| 107 | collection path or V1 map-to-`metrix` bridge remains reachable from normal |
| 108 | execution. |
| 109 | |
| 110 | - Temporary V1-to-V2 parity bridges MAY be used during development, but the |
| 111 | finished collector MUST NOT keep a runtime V1 map-to-`metrix` bridge. |
| 112 | - For migrations, first create a compatibility manifest covering chart IDs, |
| 113 | contexts, dimension IDs/names, labels, config keys, DynCfg schema keys, |
| 114 | stock config, alerts, docs, and lifecycle behavior. |
| 115 | - Migrations MUST preserve existing public contracts unless the SOW records an |
| 116 | explicit breaking decision. |
| 117 | - Migrations MUST keep old YAML/JSON field names. Add new config as opt-in when |
| 118 | cardinality, cost, or user-visible identity could surprise existing users. |
| 119 | - Collector integration artifacts MUST follow |
| 120 | `.agents/skills/integrations-lifecycle/consistency.md`; do not preserve a |
| 121 | partial local artifact checklist in V2 collector work. |
| 122 | - MUST NOT log raw secrets, DSNs, bearer tokens, or URLs with embedded |
| 123 | credentials. |
| 124 | |
| 125 | ## Hot-Path Logging |
| 126 | |
| 127 | - Collectors MUST NOT emit `Warningf`/`Errorf` every collection cycle for a |
| 128 | recoverable partial failure. Use the built-in logger limiter: |
| 129 | `c.Limit("collector:stable-operation-key", 1, time.Hour).Warningf(...)`. |
| 130 | - Limiter keys MUST be stable and low-cardinality. Use operation names, not |
| 131 | entity IDs, labels, URLs, raw errors, or user-controlled values. |
| 132 | - `Once()` is reset by `JobV2.runOnce()`, so it is useful inside one cycle only; |
| 133 | it is not cross-cycle spam protection. |
| 134 | - Full collection failure SHOULD still return an error with context so the job |
| 135 | retry path handles it. Limit only fail-soft warnings/errors where collection |
| 136 | continues with partial or stale data. |
| 137 | |
| 138 | ## Host Scopes |
| 139 | |
| 140 | - Host scopes SHOULD be used only after a product decision says the data belongs |
| 141 | on a generated vnode. |
| 142 | - `ScopeKey` and `GUID` MUST be deterministic. |
| 143 | - Collector-generated vnodes MUST set `_vnode_type=<source>`. |
| 144 | - Host-scope cardinality MUST be bounded and documented. Collectors SHOULD NOT |
| 145 | create VM/disk/NIC/path/sensor scopes by default. |
| 146 | - Scope identity MUST use stable IDs. Human-readable names SHOULD be hostnames |
| 147 | or promoted labels only. |
| 148 | |
| 149 | ## Tests |
| 150 | |
| 151 | At minimum, V2 work MUST include these tests, or the PR/SOW MUST justify why a |
| 152 | specific item does not apply: |
| 153 | |
| 154 | - config YAML/JSON serialization compatibility; |
| 155 | - `Init`, `Check`, `Collect`, and `Cleanup` lifecycle coverage; |
| 156 | - explicit metric-store cycle tests with `BeginCycle`, success commit, and abort |
| 157 | on expected collection errors; |
| 158 | - chart-template schema/decode/validate/compile coverage; |
| 159 | - chart coverage assertions for fixtures expected to materialize all dimensions; |
| 160 | - host-scope tests when scopes/vnodes are used. |
| 161 | |
| 162 | ## Pre-PR Check |
| 163 | |
| 164 | - A finished V1-to-V2 migration MUST NOT keep a runtime |
| 165 | `map[string]int64` collection path or V1 map-to-`metrix` bridge. |
| 166 | - The PR description or design note MUST enumerate affected collector |
| 167 | consistency artifacts and justify every artifact that did not need a matching |
| 168 | change. SHOULD-level exceptions and escape hatches MUST be reviewer-visible. |
| 169 | - Existing public chart/metric/config identity MUST be preserved unless the SOW |
| 170 | records an explicit breaking decision. |
| 171 | - New labels and scopes MUST be bounded and documented. |
| 172 | - Enrichment SHOULD be split from the V2 compatibility migration when possible. |