Commit Graph

49 Commits

Author SHA1 Message Date
znetsixe
a16f526964 chore(dashboardAPI): enforce min visual gap between threshold lines
User-visible problem: with the basin config dryRunThresholdPercent=2 (so
dryRunLevel ≈ outflowLevel) and highVolumeSafetyThresholdPercent=98 (so
highSafetyLevel ≈ overflowLevel), two pairs of threshold lines sat right
on top of each other in the tank visual, leaving no room between them for
their labels. The 'BELOW' fallback in the label algorithm couldn't fit
either, so labels ended up crossing lines.

Fix: enforce a minimum 28 px visual gap between adjacent threshold lines
inside the tank (≈3.7 % of the 760-tall reference frame, > LABEL_H + 2).
Lines closer than that get spread apart while preserving order. If the
stack would push the lowest line past the tank floor, the whole stack
shifts up to fit. Slight geometric distortion is accepted — the tank
visual conveys ordering and zone structure, not exact-scale level
measurement; numeric values are still rendered next to each line.

Result: at any basin geometry, labels sit cleanly above their line with
no overlap, no label-on-line collision, and no fallback to a 'stacked'
position that crosses its own line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:52:19 +02:00
znetsixe
8afc6b9779 chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.

This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
  geometry (tank, zones, threshold lines, header, footer) uses literal
  percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
  _templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
  margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
  between them (was touching) and bumps font size 11 -> 14 for readability.

Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
znetsixe
193f913eb1 chore(dashboardAPI): center tank vertically + handle floor-edge labels
Tank rectangle moved from top-aligned (top=20 in 760 frame) to vertically
centered (top=40, with 40 px top + 40 px bottom margins for the rim and
floor caption text). Header rim caption shifted to y=20, footer floor to
y=724, so both sit just outside the tank rect.

Label algorithm extended: when a label would normally go BELOW its line
but doing so would push it past the tank floor (which happens for very
small dryRunThresholdPercent — dryRunLevel sits right on outflowLevel,
both nearly at the basin floor), it falls back to stacking ABOVE the
previous label instead of extending into invisible space. This keeps
all 5 threshold labels inside the visible canvas area at the cost of a
slight visual overlap of the lowest label with its own line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 11:38:58 +02:00
znetsixe
41a20d4679 chore(dashboardAPI): center basin labels, position above/below lines
Threshold labels were sitting right on top of their lines (label center
at line_y - 8) and were right-aligned at the tank's right edge. They now:

- Sit clearly above the line (label bottom 6 px above) by default, or
  below the line (label top 6 px below) when an adjacent threshold is
  closer than 24 px (would crowd both labels above their lines). For
  the current basin config this puts overflowLevel + inflowLevel +
  dryRunLevel ABOVE their lines, and highSafety + outflowLevel BELOW.
- Are centered horizontally in the tank (name at left:115 width:95
  right-aligned, value at left:215 width:80 left-aligned) so the
  combined phrase "overflowLevel  3.22 m" reads as one centered string.

Value width 60 → 80 so 'mm'-formatted small-meter values don't wrap to
two lines. Footer floor moved to y:728 to keep clear of the BELOW labels
near the tank floor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 11:32:45 +02:00
znetsixe
8a26e17780 chore(dashboardAPI): Tank Layout fills card vertically too
Canvas frame height 600 → 760 px and tank rectangle height 520 → 680 px
so the visual fills the card aspect (taller than wide). Floor footer
moves to y=702 (was 542) to stay just below the new tank floor.

In-canvas bottom readouts (level / volume / fill mini-stats) removed —
they were redundant with the Status row Level stat, the bar gauge, and
the Level/Volume timeseries, and were getting clipped below the card's
visible area anyway. The basin canvas now shows only basin-structure
information (geometry, zones, thresholds).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 11:12:01 +02:00
znetsixe
3cd749bf37 chore(dashboardAPI): inline basin labels — tank fills card width
Tank visual now fills the Canvas card edge-to-edge instead of leaving
horizontal padding for external name + value label columns. Each
threshold's name and value sit INSIDE the tank near its line ('overflow-
Level  3.22 m', 'highSafety  3.16 m', etc.), right-aligned at the tank's
inner right edge.

Tank rectangle, zone tints, threshold lines, header rim, and footer floor
all widen from left:80 width:200 → left:10 width:380 to fill the frame.
Label colors darkened slightly (e.g. #e54343 → #c92020) to keep contrast
against the semi-transparent zone tint backgrounds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 10:59:31 +02:00
znetsixe
70151e52ec chore(dashboardAPI): Tank Layout card width matches its visual
Canvas frame logical width: 480 → 400 px (was leaving ~104 px of empty
space on the right inside the card). Panel grid width: 8 → 6 cols so the
card pixel width matches the frame logical width and content fills it
without horizontal padding, instead of letterboxing in the centre.

Bottom readouts repositioned to fit within 400 px (level/volume/fill all
inside the new frame width) and per-field decimal overrides added so unit
formatting doesn't truncate ('100.00 mm' fits in the value label width).

Freed grid cols flow to the Level + Volume timeseries on the right
(w:12 → 14 each, x:12 → 10) so the right half consumes the rest of the
row without a gap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 10:53:08 +02:00
znetsixe
b3972d4a2f chore(dashboardAPI): double basin row height for pumpingStation
Basin row grows from h:10 to h:20. Bar gauge, Canvas, and Level/Volume
timeseries all scale proportionally. Canvas internal frame doubled (480x600)
and tank rectangle stretched (height 240→520) so the canvas content fills
the panel instead of letterboxing in the top half. Bottom readouts moved
from y=280 to y=562 to stay just below the taller tank floor.

Flow row + its panels shifted down by 10 grid rows to make room.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 10:45:51 +02:00
znetsixe
3529c9f970 feat(dashboardAPI): basin canvas + bar gauge for pumpingStation
Replaces the configuration row's Heights + Volume Limits stat panels and
the radial Fill % gauge with an integrated basin visual that conveys tank
geometry and live water level at a glance.

Configuration row → Basin row:
  - Vertical bar gauge bound to level (m) with min=0/max=basinHeight and
    thresholds at outflow/dryRun/inflow/highSafety/overflow safety levels.
  - Canvas panel with tank outline, zone tints (dead/operating/highSafety/
    spill), threshold lines + named labels, and live numeric readouts for
    each threshold value plus current level/volume/fill at the bottom.
  - Level + Volume timeseries moved next to the basin visual so the row
    reads as basin → trends left-to-right.

Other layout polish:
  - Status row Fill % gauge removed; remaining 4 stats widen to w:6 each.
  - Old "Basin" row header dropped (its panels migrated into the new row).
  - Configuration row renamed to "Basin".

Mechanics:
  - dashboardAPI substitutes mustache {{var}} placeholders in templates at
    JSON.parse time. Per-softwareType var sets live in _templateVarsForNode;
    pumpingStation gets basin geometry + derived safety levels + canvas
    pixel y-positions + min-gap-enforced label positions.
  - Mustache braces stay distinct from Grafana's ${var} dashboard variables.
  - Canvas Flux query pivots heights + predicted level/volume/percent into
    one row with normalized field names so metric-value elements can bind.

No node-side telemetry change: dryRunLevel + highVolumeSafetyLevel already
reach Influx via getOutput() (specificClass.js:248,250) and outputUtils
iterates every key with no filter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 10:32:52 +02:00
znetsixe
90536d631d fix(dashboardAPI): MGC dashboard — drop dead Scaling panel, show group Mode/RelDistPeak
- Remove the "Scaling" stat panel: it queried field 'scaling' that machineGroup
  never emits, so it always rendered "No data".
- The "Mode" and "Rel Dist Peak" panels were stripped by the #39 no-duplication
  rule because child pumps emit fields of the same name ('mode', 'relDistFromPeak').
  But those are the GROUP's own measurement, never a true duplicate of per-pump
  series. Mark them emittedFields:[] (the existing "always keep" convention used by
  the injected pump panels) so the group-level status/metric renders.

Verified live: MGC dashboard now shows Mode "optimalControl", Abs/Rel Dist Peak.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:47:57 +02:00
znetsixe
c4f5b68c6a fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
Three display defects surfaced when rendering the live PS/pump/MGC dashboards:

1. Doubled values everywhere. EVOLV telemetry historically carried stray tags
   (tagcode="undefined"/uuid="null") that newer writes dropped, so InfluxDB holds
   two series per field and last() returned two of everything (e.g. Time Left,
   Runtime, Heights). Add |> group(columns:["_field"]) before last()/aggregateWindow
   in every template query so each field collapses to one value/line regardless of
   tag-set history.

2. fields:"/.*/" also rendered the _time column as a stat value. For single-field
   string panels (Direction, Flow Source, Mode, State, Prediction Quality) append
   |> keep(columns:["_value"]); for mixed string+numeric panels (valve/vgc/monster)
   drop _time/_start/_stop instead.

3. Level/Heights showed "0.12 min" — Grafana unit id "m" means minutes, not meters.
   Change to lengthm; normalize m³->m3, m³/h->m3/h on pumpingStation.

Verified live via headless screenshots: PS, pump, and measurement dashboards now
show single clean values with correct units.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:42:47 +02:00
znetsixe
8bfc67c610 fix(dashboardAPI): show string fields in stat panels (reduceOptions.fields)
Grafana Stat/Gauge panels default the Fields option to "Numeric Fields"
(reduceOptions.fields == ""), so string-valued fields (mode, state,
movementState, direction, flowSource, predictionQuality, running) are excluded
and the panel renders "No data" even though the Flux query (last()) returns the
string correctly.

Set reduceOptions.fields = "/.*/" ("All fields") on every stat panel bound to a
string field across machine, machineGroup, pumpingStation, valve,
valveGroupControl, and monster templates. lastNotNull calc was already correct.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:09:24 +02:00
znetsixe
5d651b59ef feat(dashboardAPI): resolve Grafana folder by name (fixes stale folderUid 400s)
A pinned folderUid goes stale whenever Grafana is rebuilt — the same-named
folder returns with a fresh uid and every dashboard upsert then 400s
"folder not found", silently dropping all generated dashboards.

Add a folderTitle config field: when set, resolveFolderUid() looks the folder
up by name (GET /api/folders), creates it if absent (POST /api/folders),
caches the uid for the process, and falls back to the configured folderUid on
any failure (never worse than the pinned behavior). The emit handlers
(registerChild/regenerateDashboard/emitDashboardsFor) are now async and await
the resolution. folderUid retained as an explicit override/fallback.

Locked by slice48-folder-resolve-by-name; existing emit tests made async.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:02:38 +02:00
znetsixe
5533293647 feat(dashboardAPI): slice47 MGC pump panel telemetry + tests
- specificClass updates for MGC per-pump panel sources.
- Output manifest + slice47 basic test for the pump-panel outputs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 16:09:29 +02:00
znetsixe
990a8c09ea feat(dashboardapi): recursive subtree discovery + measurement-name/template parity
Generate dashboards for an entire parent-child subtree from a single root
registration (pre-order, cycle/diamond-safe), so wiring only the subtree root
(e.g. pumpingStation) to dashboardAPI yields dashboards for every descendant.

Fix two contract drifts that left generated panels blank against live telemetry:
- _measurement var now mirrors outputUtils.formatMsg (general.name ||
  <softwareType>_<id>); previously it always used the fallback form, so any
  named node's dashboard queried a non-existent series.
- pumpingStation template field keys realigned to emitted telemetry
  (flow.*.{upstream,out,overflow}, netFlowRate.measured, inflowLevel/
  outflowLevel/overflowLevel, maxVolAtOverflow/minVolAt{Inflow,Outflow}).

Adds template alias resolution (softwareType -> shared template file) and
locks parity with slice44/45/46 tests + output manifest. 67/67 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:45:37 +02:00
dc08c85409 docs(dashboardapi): output-coverage manifest + populated/degraded tests (#43)
Per .claude/rules/output-coverage.md every node ships test/_output-manifest.md
enumerating every output across every state. This manifest covers all the
outputs added by slices #34-#42 in this PRD:

- Port 0 upsert message: every key (topic, url, method, headers, payload,
  meta) with type and tested states.
- Port 1: explicit "not used" with rationale.
- Port 2: explicit "not used" with rationale.
- Structured log outputs: 5 events (regen-emitted, regen-skipped,
  manual-regen-requested, parent-panels-deduped, flows:started) with
  fields and corresponding test.
- specificClass return shapes: 6 methods with populated + degraded states.
- Anti-patterns enforced: no payload:null, absent vs null discipline,
  tab id avoidance in predicate.

- test/_output-manifest.md: the manifest.
- test/basic/slice43-output-manifest.basic.test.js: 6 cross-cutting tests
  exercising populated AND degraded states (token absent, folderUid absent,
  template missing, diff-skip, regen logging, manual regen).

Backfill manifests for other nodes tracked in IMPROVEMENTS_BACKLOG.

Closes #43
2026-05-26 18:08:48 +02:00
2b745dfb51 example(dashboardapi): basic.flow.json demos end-to-end Grafana round-trip (#42)
Replaces the placeholder inject→dashboardapi→debug example with the full
chain: inject (simulating a measurement child registration) → dashboardapi
(composes dashboard JSON) → http request (POSTs to Grafana) → debug (shows
the response). Default targets http://grafana:3000 inside the Docker compose
network. Configure bearer token via the encrypted credentials field.

Refs #42
2026-05-26 18:06:54 +02:00
3c8427ed7a feat(dashboardapi): manual regen via msg.topic == regenerate-dashboard (#41)
Adds an explicit topic for operators (and the dashboardAPI v2 manual escape
hatch from PRD F-12). On `regenerate-dashboard`, dashboardAPI iterates every
child source cached by prior `child.register` messages and re-emits Grafana
upsert messages — bypassing the diff-skip predicate from #36.

- src/specificClass.js: light state cache (recordChild / cachedChildSources).
- src/commands/handlers.js: refactor shared emit path; emitDashboardsFor()
  used by both child.register and regenerateDashboard; meta.trigger
  distinguishes the two for downstream filtering.
- src/commands/index.js: register 'regenerate-dashboard' (alias 'regen').
- CONTRACT.md: document the new topic.
- test/basic/slice41-manual-regen.basic.test.js: 5 cases covering cache
  semantics, no-op for empty cache, bypass-predicate, trigger stamp on both
  paths, registry exposure.

Closes #41
2026-05-26 18:05:31 +02:00
8964b0b638 feat(dashboardapi): MGC template polish — group-level only + dashed bounds (#40)
- config/machineGroup.json: every non-row panel now annotated with
  meta.emittedFields (mode, scaling, abs/relDistFromPeak, flow.total/group,
  power.total/group). Per-pump fields (ctrl, state, runtime, pressure,
  temperature) deliberately absent — those live on rotatingMachine children
  per #39's no-data-duplication contract.
- Timeseries panels gain byRegexp dashed-bounds overrides for .min$/.max$
  (same pattern as #38).
- test/basic/slice40-mgc-template.basic.test.js: 4 cases — no per-pump
  fields leak in, every non-row annotated, dashed overrides present on TS,
  composer dedup applies when a child claims an MGC-level field.

Closes #40
2026-05-26 18:03:28 +02:00
a76f22281e feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.

- config/pumpingStation.json: 11 non-row panels annotated with
  meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
  Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
  filter after composing children; row panels always kept; panels without
  emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
  presence, child-covered removal, no-overlap preservation, row preservation.

Closes #39
2026-05-26 18:01:58 +02:00
e5099de986 feat(dashboardapi): dashed .min/.max overrides on rotatingMachine panels (#38)
Applies the byRegexp(\\.min$ | \\.max$) → custom.lineStyle dashed pattern to
all 4 timeseries panels in config/machine.json — pattern confirmed via S2
spike (#33). Forward-compatible: nodes that don't yet emit .min/.max fields
see no change in rendering (regex won't match).

- config/machine.json: 4 timeseries panels gain byRegexp overrides for both
  .min$ and .max$, dashed [10,10], orange (min) / red (max).
- test/basic/slice38-dashed-bounds.basic.test.js: 2 cases (presence per ts
  panel, anchor-to-end forward compatibility).

Companion-field emission helper (generalFunctions.outputUtils — produces
<field>, <field>.min, <field>.max from a bounds-aware source) is a
generalFunctions submodule change and lands in a follow-up PR — out of
scope for this dashboardAPI-only slice.

Closes #38
2026-05-26 18:00:40 +02:00
8639b02e6a feat(dashboardapi): emittedFields metadata for parent-panel dedup (#37)
Adds per-panel `meta.emittedFields` to machine.json (rotatingMachine) and
machineGroup.json (MGC) templates. Each non-row panel declares the Influx
field paths it visualizes, so a parent template's composer can filter out
panels already covered by its children (#39 no-data-duplication rule).

- config/machine.json: 13 non-row panels annotated.
- config/machineGroup.json: panels annotated.
- src/specificClass.js: collectEmittedFields(dashboard) helper.
- test/basic/slice37-emitted-fields.basic.test.js: 4 cases (template loads
  with annotations, aggregation, missing-meta graceful, null input).

PRD F-6 panel set audit: machine.json already covers all the PRD-required
panels (State/Mode/Ctrl%/Runtime/NCog%/Flow/Efficiency/Pressure/Temperature/
Diagnostics) — substantially more than asked. No new panels added.

PRD F-7 predicted-vs-measured side-by-side: deferred. Current architecture
is "1 dashboard per node" (each child gets its own dashboard, cross-linked
from the parent), not "1 dashboard with N composed panels." Side-by-side
rendering of predicted (rotatingMachine dashboard) + measured (measurement
child dashboard) lives naturally as drill-down navigation today. Refactor
to a single-dashboard composition model would be substantial — flagged in
the issue comment for v2 if the drill-down UX proves insufficient.

Closes #37
2026-05-26 17:59:37 +02:00
aac71eb129 feat(dashboardapi): diff-skip regen via flows:started predicate (#36)
Subscribes to Node-RED's flows:started runtime event, caches the {diff}
payload on the dashboardAPI source, and short-circuits the child.register
handler when none of {dashboardAPI id, child id, grandchild ids} appears in
diff.added/changed/removed/rewired. Predicate verified by S1 spike (#32).

- src/nodeClass.js: _attachLifecycleHook subscribes, _attachCloseHandler
  cleans up. No-op when RED.events isn't available (unit-test friendly).
- src/specificClass.js: subtreeChanged() predicate + subtreeIdsFor() helper.
- src/commands/handlers.js: registerChild consults predicate before composing;
  logs {event:'regen-skipped', outcome:'no-diff'} when skipping.
- test/basic/slice36-diff-predicate.basic.test.js: 8 cases — null/empty diff,
  affected/unaffected ids, tab-id over-triggering avoidance, grandchild
  inclusion, no-grandchild case.

Cold start (no cached diff yet) always regenerates — safe default. Edge case
documented in #32: when a brand-new child is wired to a registered parent,
the new child's id is in diff.added but not yet in registeredChildren when
flows:started fires. Mitigation (b) per spike findings: one-deploy race
accepted for R&D — next deploy picks up the new registration.

Closes #36
2026-05-26 17:57:34 +02:00
bdf87ffd67 test(dashboardapi): perf + uid-uniqueness for multi-child composition (#35)
Architectural note: existing composition is "1 dashboardAPI → root dashboard
+ 1 per child", not "1 dashboardAPI → 1 dashboard with N panels" as the PRD
assumed. Each generated dashboard is laid out at template-authoring time
(explicit gridPos per panel inside config/<softwareType>.json); the composer's
job is to substitute per-instance templating variables and assemble the
cross-link list. So the PRD's "non-overlapping gridPos for N panels" lands as:

- perf: 50 children compose in <500ms (PRD N-1).
- uid-uniqueness: stableUid keyed on softwareType:nodeId never collides.
- byte-identical idempotency (PRD N-2): two consecutive compositions match.
- root links: one link per registered child.

No production code change — this slice just adds the perf/uniqueness/idempotency
guarantees as explicit tests so we can't regress.

Closes #35
2026-05-26 17:55:39 +02:00
7fdab73ba0 feat(dashboardapi): walking skeleton for graph-aware Grafana generator (#34)
Encrypts the Grafana bearer token via Node-RED credentials block instead of
plain config (F-11). Adds folderUid config field threaded through to the
buildUpsertRequest payload (F-8, resolves PRD O-5). Migration path: legacy
plain bearerToken still loads, with one-time warn() prompting user to re-save.

Composition + URL + headers + per-instance UID were already in place; only
the credentials + folderUid + tests are new.

- dashboardAPI.html: bearerToken moved to credentials block; folderUid added.
- dashboardAPI.js: registerType options pass credentials descriptor.
- src/nodeClass.js: read token from node.credentials; legacy fallback warns.
- src/specificClass.js: buildUpsertRequest emits folderUid when set.
- src/commands/handlers.js: pass folderUid from config to buildUpsertRequest.
- test/basic/slice34-credentials-and-folder.basic.test.js: 5 new tests.

Diff-based regeneration (F-1) and the explicit flows:started lifecycle hook
land in #36 once the S1 spike predicate is wired. Until then, the existing
child.register message trigger continues to drive composition on every
startup-time child registration.

Closes #34
2026-05-26 17:53:42 +02:00
znetsixe
dac8576cab style: palette swatch → (domain-hue redesign 2026-05-21)
Sidebar swatch now follows function family rather than S88 level, so the
palette is visually identifiable instead of monochromatically blue. Editor-group
rectangles in flow.json still follow S88 — only the registerType color changed.
Full table + rationale: superproject .claude/rules/node-red-flow-layout.md §10.0
and .claude/refactor/OPEN_QUESTIONS.md (2026-05-21 entry).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:05:59 +02:00
znetsixe
e04c4a1132 fix: rename dashboardapi.{js,html} → dashboardAPI.{js,html}
Aligns the entry-file naming with the folder-name convention from
.claude/rules/node-architecture.md / superproject CLAUDE.md.

NON-BREAKING: the Node-RED type id stays lowercase
(`RED.nodes.registerType('dashboardapi', ...)`) so every deployed flow
that references this node continues to load. Only the file paths
change. Admin endpoints `/dashboardapi/menu.js` and
`/dashboardapi/configData.js` are unaffected — they follow the type
id, not the filename.

Updated:
- package.json `main` + `node-red.nodes` value
- test/basic/structure-module-load.basic.test.js require path
- CLAUDE.md: legacy-drift warning replaced with a note explaining the
  type-id preservation strategy

Same approach recommended for the remaining two legacy renames (mgc,
vgc); the superproject CLAUDE.md drift table now spells that out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:36:56 +02:00
znetsixe
0b857ef444 fix: align logging.enabled default to schema (true)
dashboardapiConfig.json declares `logging.enabled.default: true` but:
- dashboardapi.html defaulted `enableLog: false`
- src/specificClass.js `_buildConfig` used `Boolean(...)` which
  coerced undefined to false, overriding the schema default.

Aligned both to the schema: HTML default = true; _buildConfig now
uses `?? true` so an unspecified config follows the schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:59:19 +02:00
znetsixe
fb5a9ebff8 docs(wiki): regenerate topic-contract AUTOGEN block via wiki-gen
Replaces the agent-written placeholder inside Reference-Contracts.md with
the authoritative table generated from src/commands/index.js. Both the
BEGIN and END markers are normalized to the canonical form used by
`@evolv/wiki-gen`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:11:47 +02:00
znetsixe
a9fc51d6f0 docs(wiki): full 5-page wiki matching the rotatingMachine reference format
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>
2026-05-19 09:42:14 +02:00
znetsixe
a6f09d821d docs: Folder & File Layout section + flag dashboardapi.{js,html} naming drift
Entry/HTML files should be dashboardAPI.{js,html} (case-sensitive match with
the folder name). Rename when the file is next touched. Full rule:
.claude/rules/node-architecture.md in the EVOLV superproject.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:30:46 +02:00
znetsixe
f0a7904985 P11.7 wiki: rewrite Home.md to full 14-section visual-first template
Adapts the canonical WIKI_TEMPLATE.md for dashboardAPI as a utility node
(no BaseDomain, no S88 level, no state chart). Key changes vs P9.3 draft:
- Banner hash bumped to 7b3da23
- Section 1: tightened to exactly describe topology→dashboard flow
- Section 2: adds FlowFuse/browser as downstream consumer of Grafana dashboards
- Section 3: expands capabilities (stable UID, bucket-per-position, alias alias)
- Section 4: adds dashboardapi.js entry node + real config/ template list
- Section 5: AUTOGEN markers regenerated via npm run wiki:all
- Section 6: rewrites diagram with resolveChildSource detail
- Section 7: full sequence including stableUid + links[] step
- Section 8: AUTOGEN marker regenerated; adds meta-field table
- Section 9: adds enableLog/logLevel fields; adds bucket-fallback table
- Section 10: explicit SKIPPED marker (stateless node)
- Section 11: adds inline wiring example
- Section 12: expands to 7 recipes (adds UID-change, machineGroupControl alias)
- Section 13: adds "not a BaseDomain node" + OPEN_QUESTIONS reference
- Section 14: adds OPEN_QUESTIONS.md link for BaseDomain decision; keeps 5 issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:06:42 +02:00
znetsixe
7b3da23fba P11.6 wiki regen + Phase 10 private-test rewrites where applicable
For all 11 nodes with auto-gen markers: wiki/Home.md sections 5 (topic
contract) and 9 (data model) regenerated via npm run wiki:all. New
Unit column shows '<measure> (default <unit>)' for declared topics,
'—' otherwise. Effect column now uses descriptor.description (P11.2
field) overriding the generic per-prefix fallback.

For rotatingMachine + reactor: Phase 10 test rewrites — 3 + 8 files
moved off private nodeClass internals (_attachInputHandler, _commands,
_pendingExtras, _registerChild, _tick, etc.) to the public
BaseNodeAdapter surface (node.handlers.input, node.source.*).
+6 / +7 net new tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:44:02 +02:00
znetsixe
67a374ff4f P9.3: wiki/Home.md following 14-section visual-first template + wiki:* scripts
Auto-generated topic-contract + data-model sections via shared wikiGen
script. Hand-written Mermaid diagrams for position-in-platform, code
map, child registration, lifecycle, configuration, state chart (where
applicable).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:17:46 +02:00
znetsixe
92d7eba0fd P10.2: convert remaining dashboardAPI tests from Mocha to node:test
P6.7 converted test/basic/. Convert test/edge/ and test/integration/ the
same way: describe/it/expect → test/assert. No behavioural change.

5 / 5 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:44:15 +02:00
znetsixe
2874608375 P6: convert dashboardAPI to platform infrastructure
Refactor of dashboardAPI to use BaseNodeAdapter + commandRegistry + statusBadge.
dashboardAPI follows the platform refactor plan in .claude/refactor/MODULE_SPLIT.md.
Tests stay green; CONTRACT.md generated; legacy aliases preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:23:45 +02:00
znetsixe
869ba4fca5 docs: add CLAUDE.md with S88 classification and superproject rule reference
References the flow-layout rule set in the EVOLV superproject
(.claude/rules/node-red-flow-layout.md) so Claude Code sessions working
in this repo know the S88 level, colour, and placement lane for this node.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 07:48:20 +02:00
Rene De Ren
66b91883ac Fix dashboardapi adapter and Jest coverage 2026-03-12 16:46:50 +01:00
Rene De Ren
c5272fcc24 Adopt buildConfig in dashboardapi adapter 2026-03-12 16:43:29 +01:00
znetsixe
89d2260351 updates 2026-03-11 11:13:44 +01:00
znetsixe
547333be7d working 2026-02-23 13:16:58 +01:00
znetsixe
b285d8e83a before functional changes by codex 2026-02-19 17:37:36 +01:00
znetsixe
1ea4788848 update dashboardAPI -AGENT 2026-01-13 14:29:43 +01:00
znetsixe
c99a93f73b removed error inducing module deprecated 2025-11-13 19:38:09 +01:00
znetsixe
5f1dd7675c dashboard update S88 2025-10-14 14:08:41 +02:00
znetsixe
b66e014a46 updated 2025-09-22 16:04:19 +02:00
znetsixe
1610a3bcc9 license update 2025-08-07 13:56:52 +02:00
znetsixe
ac3bdd76eb changes 2025-05-26 17:44:56 +02:00
RnD
5d78bd4d84 Initial commit 2025-05-13 09:53:25 +00:00