Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
Reference — Limitations
Note
What
dashboardAPIdoes not do, current rough edges, and open questions. Open items live in.agents/improvements/IMPROVEMENTS_BACKLOG.mdand.claude/refactor/OPEN_QUESTIONS.mdin the superproject.Pending full node review (2026-05). Content reflects
CONTRACT.mdand current source only.
When you would not use this node
| Scenario | Use instead |
|---|---|
| You maintain Grafana dashboards by hand | Skip dashboardAPI — it will overwrite your customisations on every child.register (upsert is overwrite: true). |
| You need arbitrary Grafana API calls (annotations, alerts, data sources, folders) | A plain http request node. dashboardAPI only emits POST /api/dashboards/db envelopes. |
| You want to forward tick / measurement data to Grafana | This is not what dashboardAPI does. Wire telemetry through Port 1 of an EVOLV process node directly into InfluxDB; Grafana queries InfluxDB. |
| You want to use dashboardAPI as a BaseDomain-capable child of something else | Not supported — dashboardAPI does not extend BaseDomain and cannot register as a child of machineGroupControl / pumpingStation / similar. See No BaseNodeAdapter / BaseDomain below. |
| You expect EVOLV nodes to auto-discover dashboardAPI | They don't. Port 2 of the emitter must be wired into dashboardAPI's input explicitly. |
Known limitations
Legacy filename drift
The entry file and editor HTML are currently lowercase — dashboardapi.js and dashboardapi.html — rather than dashboardAPI.js / dashboardAPI.html per the canonical folder-name convention in .claude/rules/node-architecture.md.
The convention rule explicitly calls this out as legacy drift to fix when the file is next touched. A rename is a four-touch change:
dashboardapi.js→dashboardAPI.jsdashboardapi.html→dashboardAPI.htmlpackage.json#node-red.nodes— key remainsdashboardapi(the Node-RED type id is independent of the filename) but the value becomesdashboardAPI.js.- Superproject submodule references and any
require()paths.
The Node-RED type id (dashboardapi, lowercase, registered via RED.nodes.registerType('dashboardapi', …)) must stay dashboardapi to avoid breaking existing flows in the wild. The rename is purely the source-file path. Tracked.
Example flow stubs
The three shipped flows (basic.flow.json, integration.flow.json, edge.flow.json) are placeholders. Their inject nodes don't fire a payload that matches the child.register resolver:
| File | Current behaviour | What's wrong |
|---|---|---|
basic.flow.json |
Inject topic: 'ping' |
Not child.register; registry silently drops. |
integration.flow.json |
Inject topic: 'registerChild' with payload: 'example-child-id' (string) |
The string id has no live Node-RED node behind it; RED.nodes.getNode('example-child-id') returns null; throws 'Missing or invalid child node'. |
edge.flow.json |
Inject topic: 'doesNotExist' |
Works as a registry-coverage probe (silent drop is correct) but exercises nothing. |
Working wiring patterns are documented inline in Reference — Examples. Replacement of the stubs is tracked in IMPROVEMENTS_BACKLOG.md (P9 wiki cleanup follow-up).
No BaseNodeAdapter / BaseDomain
Most EVOLV nodes inherit a common adapter / domain base class. dashboardAPI does not. The decision is recorded in OPEN_QUESTIONS.md (2026-05-10) — four blockers (no platform config JSON, no periodic output, no parent registration, no status badge / tick / measurements). Until BaseNodeAdapter grows passive-mode flags (skip-registration + skip-output-stream), the bespoke adapter shape is the correct compromise.
Consequence: dashboardAPI cannot be introspected via the standard getOutput() channel. Debugging relies on watching Port 0 in a debug node.
No domain output / no manifest
Per .claude/rules/output-coverage.md, every node should ship a test/_output-manifest.md enumerating every Port-0/1/2 key in populated and degraded states. dashboardAPI's output surface is one envelope shape, emitted only when a dashboard is successfully generated — there is no degraded "partial envelope" state to test. The manifest collapses to:
| Port | Output | Populated state | Degraded state |
|---|---|---|---|
| 0 | {topic, url, method, headers, payload, meta} envelope |
Emitted once per generated dashboard | Not emitted — on resolution failure the handler throws and nodeClass sets a red status badge instead |
| 1 | (unused) | — | — |
| 2 | (unused) | — | — |
The full output-coverage rule applies prospectively; no backfill manifest exists yet. Tracked.
Template discovery is filename-based
The template lookup is softwareType ↔ filename. Renaming a node's softwareType (e.g. rotatingmachine → rotatingMachine) requires either renaming the template file or adding an alias arm in _templateFileForSoftwareType. The machineGroupControl → machineGroup.json mapping is a one-off alias because the historical filename was abbreviated.
Note
Verify in full review: which softwareType does the current
rotatingMachineemit? The shipped template isconfig/machine.json— ifrotatingMachine'sfunctionality.softwareTypeis'rotatingmachine'(lowercase), the case-insensitive fallback won't find it and dashboard generation will warn-and-skip. Flagged.
No retry / circuit-breaker on downstream HTTP
dashboardAPI emits the upsert envelope and is done. If the downstream http request node fails (Grafana down, 5xx, network timeout), the dashboard upsert is silently dropped — no retry, no DLQ, no status badge propagation back to dashboardAPI. The caller is responsible for wiring retry logic into the http-request path.
oneditsave doesn't read all editor fields uniformly
dashboardapi.html oneditsave reads ['name', 'protocol', 'host', 'port', 'bearerToken', 'defaultBucket'] via direct DOM lookups, separately from the logger menu's saveEditor. Adding a new editor field requires touching both the form HTML and the oneditsave whitelist. Mild; not load-bearing.
Config default mismatch
The runtime _buildConfig defaults general.logging.enabled to Boolean(config?.general?.logging?.enabled) — effectively false when the editor doesn't set it. But dependencies/dashboardapi/dashboardapiConfig.json declares the default as true. The editor menu (loggerMenu) bridges these via the standard EVOLV logger pattern, but the divergence is worth confirming — logger enabled vs disabled changes whether Skipping dashboard generation: no template … warns appear at all.
Note
Confirm in full review which side wins by default for a freshly-dropped node. Flagged.
bucket resolution priority is global-then-per-position
buildDashboard reads this.config.defaultBucket || this.config.bucketMap[position] || defaultBucketForPosition(position). The global override fires before the per-position map. If you want per-position buckets to win over a global default, the current code doesn't do that — you'd need to leave defaultBucket empty and rely solely on bucketMap + the position fallback.
Open question whether the "global beats per-position" priority is the intended semantics. Flagged.
No InfluxDB bucket validation
The bucket name is templated into the Grafana dashboard JSON without any check that the bucket exists in InfluxDB. A typo produces a dashboard that renders panels saying "no data" with no upstream warning. Tracked.
Open questions (tracked)
| Question | Where it lives |
|---|---|
Should BaseNodeAdapter grow a passive / HTTP-only mode (skip-registration + skip-output-stream) so dashboardAPI can extend it? |
.claude/refactor/OPEN_QUESTIONS.md (2026-05-10) — "dashboardAPI skipped BaseNodeAdapter + BaseDomain" |
Confirm rotatingMachine softwareType ↔ config/machine.json mapping |
Internal — flag during full review |
Bucket priority: should per-position bucketMap beat global defaultBucket? |
Internal |
| Should dashboardAPI emit a Port-2 status / health pulse so other EVOLV nodes can detect it? | Internal |
Should child.register aliases include older topic names (e.g. RegisterChild, register-child) for legacy compat? |
Internal |
Add an explicit child.unregister / dashboard.delete topic to remove orphaned Grafana dashboards |
Internal |
Provide a programmatic way to bulk-regenerate all dashboards for an existing deployment (e.g. cmd.regenerate-all) |
Internal |
| Retry / DLQ for failed Grafana upserts | TBD |
Migration notes
From the registerChild alias
The canonical topic since 2026-Q1 is child.register. The registerChild alias still works but logs a one-time deprecation warning on first use. Migrate callers when convenient:
- msg.topic = 'registerChild';
+ msg.topic = 'child.register';
Both topics accept identical payloads.
From bare-string node-id payloads
The handler resolves bare-string payloads via RED.nodes.getNode(id) → node._flow.getNode(id) → null. This works at runtime but is brittle for tests and for flows where the emitter and dashboardAPI live on different _flow instances. Prefer the inline {source: {config: {...}}} or {config: {...}} shapes for tests and for any flow that imports both sides as JSON (no RED.nodes registry at compile time).
From hand-curated Grafana dashboards
If you're moving from hand-curated dashboards to dashboardAPI-generated ones:
- Export your existing dashboard JSON from Grafana.
- Replace the templating-var values for
measurementandbucketwith placeholders. - Save as
nodes/dashboardAPI/config/<softwareType>.json. - The next
child.registerfor that softwareType will upsert (overwrite) the existing dashboard, preserving the UID if you set it to matchstableUid(softwareType:nodeId).
If you want to preserve the UID of an existing hand-curated dashboard, compute sha1(softwareType:nodeId).slice(0, 12) and check it matches your existing UID. If not, either rename the node id, or accept that the first upsert will create a new dashboard alongside the old one.
Related pages
| Page | Why |
|---|---|
| Home | Intuitive overview |
| Reference — Contracts | Topic + payload resolution + envelope shape |
| Reference — Architecture | Code map, lifecycle, "no BaseNodeAdapter" rationale |
| Reference — Examples | Shipped flows + debug recipes + working wiring patterns |
| EVOLV — Open Questions | Cross-node open questions and decisions log |