feat: add Node UI metrics collection toggle - #1892
Conversation
| try { | ||
| resolveMetricsCollectorConfig({ | ||
| telemetry: { metrics: { collectionEnabled } }, | ||
| } as unknown as Pick<DkgConfig, 'telemetry'>, {}); |
There was a problem hiding this comment.
🟡 Issue: Validate the raw toggle without fabricating a typed runtime config
What's wrong
The doctor check operates on untrusted parsed config, but this change forces it through the daemon runtime type by constructing a fake config object and casting it. That muddies the boundary between raw config validation and resolved runtime config, and it makes future toggle validation likely to grow more nested shape traversal and more casts instead of using one clear parser.
Example
resolveMetricsCollectorConfig({ telemetry: { metrics: { collectionEnabled } } } as unknown as Pick<DkgConfig, 'telemetry'>, {}) is a sign that the reusable boundary is one level too high for raw config validation.
Suggested direction
Move the boolean parsing/validation into a small canonical helper, for example parseMetricsCollectionEnabledConfigValue(value: unknown): boolean, and keep resolveMetricsCollectorConfig as the daemon-facing env-precedence adapter.
For Agents
In packages/cli/src/metrics-collector-config.ts, extract/export a raw-value parser or validator for telemetry.metrics.collectionEnabled; have resolveMetricsCollectorConfig call it, and have config-sanity.ts call it directly against parsed JSON. Preserve env precedence in daemon startup and prove invalid raw config still produces the same doctor finding.
| ); | ||
| metricsCollector.start(); | ||
| log("Metrics collector started (30s interval)"); | ||
| let metricsCollector: MetricsCollector | undefined; |
There was a problem hiding this comment.
🟡 Issue: Keep the collector toggle inside a focused lifecycle helper
What's wrong
The PR adds the new feature flag by threading nullable collector state through a 3.8k-line daemon function. That preserves the existing sprawl and adds another conditional lifecycle concern at the top level instead of making the metrics collector a self-contained subsystem.
Example
A reader now has to connect the early metricsCollectorConfig, the conditional constructor, route-time optional collector use, and optional cleanup to understand the disabled state. That is more daemon-level state for a feature that could own its own start/stop policy.
Suggested direction
Replace the top-level nullable resource pattern with a small startMetricsCollector(...) or createLocalMetricsCollectorLifecycle(...) abstraction that resolves the toggle, starts when enabled, formats/logs startup, and exposes a uniform stop() cleanup handle.
Confidence note
This is a structural concern in an already very large function; the added lines are small, but they add another piece of cross-cutting lifecycle state instead of shrinking the collector ownership boundary.
For Agents
In packages/cli/src/daemon/lifecycle.ts, extract the local Node UI metrics collector lifecycle into a focused helper near the metrics section, returning the collector passed to node-ui routes plus a stop no-op when disabled. Preserve default enabled behavior, disabled no-start behavior, the startup log text, and cleanup behavior.
| metricsCollector.start(); | ||
| log("Metrics collector started (30s interval)"); | ||
| let metricsCollector: MetricsCollector | undefined; | ||
| if (metricsCollectorConfig.enabled) { |
There was a problem hiding this comment.
🟡 Issue: Daemon collector toggle is not covered at the wiring point
What's wrong
The operator-facing promise is that disabling local collection stops new SQLite snapshots and store scans. The current tests verify that the config helper returns { enabled: false }, but they do not verify that runDaemonInner uses that value to suppress the MetricsCollector side effect. That leaves the changed behavior unverified at the point where the store scans are actually started.
Example
A regression that left metricsCollector.start() unconditional in runDaemonInner, or accidentally ignored telemetry.metrics.collectionEnabled: false before this block, would still pass the new resolver and doctor tests. A focused test could mock @origintrail-official/dkg-node-ui's MetricsCollector, start runDaemonInner with { telemetry: { metrics: { collectionEnabled: false } } }, abort after startup like the existing daemon wiring tests, and assert the constructor/start were not called and the disabled startup log was emitted.
Suggested direction
Add a small daemon-level wiring regression test instead of relying only on the pure config resolver tests.
For Agents
Look at packages/cli/test/*wiring*.test.ts for the existing runDaemonInner mock harness. Add coverage around packages/cli/src/daemon/lifecycle.ts that preserves default enabled startup, proves collectionEnabled: false skips MetricsCollector construction/start, and ideally proves the env override path reaches the same daemon wiring.
Adversarial review —
|
Summary
telemetry.metrics.collectionEnabledfor the local Node UI SQLite metrics collectorDKG_METRICS_COLLECTION_ENABLEDenvironment override with config precedenceMetricsCollectorwhen collection is disableddkg doctorScope
This is intentionally the minimal enable/disable change. It does not change the existing 30-second collector interval, collection scheduling, presence gating, or
DKG_METRICS_ALWAYS_COLLECT=1behavior.The follow-up cadence PR is #1891.
Configuration
{ "telemetry": { "metrics": { "collectionEnabled": false } } }The environment override accepts
1,0,true, orfalse:export DKG_METRICS_COLLECTION_ENABLED=0Validation
tsc --noEmitpassedgit diff --checkpassed