Files
EVOLV/wiki/Topic-Conventions.md
znetsixe 5ae8788fd7 wiki: crisp overhaul — no decoration emoji, all 9 master pages refactored
Source-tree mirror of EVOLV.wiki.git refactor (27a42ee on wiki.git):

- 7 master pages rewritten with clean design (Home, Architecture,
  Topology-Patterns, Topic-Conventions, Telemetry, Getting-Started,
  Glossary). Tables and Mermaid for visuals, gitea alert callouts for
  warnings, shields badges for metadata only. No emoji as decoration.
- Archive.md becomes a removal-changelog pointing readers to git
  history and to the successor pages.
- _Sidebar.md updated to navigate the new flat-name layout.
- Concept / finding / manual pages: uniform mini-header (badges +
  "reference page" callout) added without rewriting domain content.
- Every internal link now uses the flat naming that resolves on the
  live gitea wiki (Concept-ASM-Models, Finding-BEP-..., etc.).

On wiki.git: 29 Archive-* pages hard-deleted (the git history
preserves them; Archive.md documents the removal).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:24:51 +02:00

11 KiB

Topic Conventions

code-ref source

Note

Every msg.topic in EVOLV uses one of six prefixes. The prefix tells you the kind (setter vs trigger vs data vs query vs lifecycle vs event), not the target. Units are coerced before handlers run. S88 colours are mandatory in every diagram. Source of truth: .claude/refactor/CONTRACTS.md §1.


The six prefixes

flowchart LR
    ui["UI / parent / driver / function node"]
    node[Node]
    child["Child node"]
    ext["external consumers"]

    ui -- "set. / cmd. / query." --> node
    node -. "evt." .-> ui
    node -. "evt." .-> ext
    child -- "data." --> node
    node -- "data." --> child
    child <-->|child.register| node

    class ui,ext neutral
    class node tier3
    class child tier1

    classDef neutral fill:#dddddd
    classDef tier3 fill:#50a8d9,color:#000
    classDef tier1 fill:#a9daee,color:#000

Inbound (the node accepts on its input)

Prefix Idempotent Meaning Examples
set.<noun> Yes Setter. Replaces a state value with the supplied payload. Repeating with the same payload does nothing extra. set.mode, set.scaling, set.demand, set.inflow
cmd.<verb> No Imperative action. Triggers a transition or sequence. Repeating triggers it again (or is rejected). cmd.startup, cmd.shutdown, cmd.estop, cmd.calibrate
data.<noun> n/a (values flow) Bulk data input. Sensor readings, measurement values, raw streams. The node consumes them. data.measurement, data.flow, data.pressure
query.<noun> Yes (read-only) Synchronous query. The node responds on the same msg (or a sibling output). For dashboards / debug. query.curves, query.cog, query.snapshot
child.<verb> n/a (plumbing) Parent / child plumbing. Routed via Port 2. child.register, child.unregister

Outbound (the node emits)

Prefix Meaning Where it appears
evt.<noun> Event. A fact about something that just happened. Fire-and-forget — no consumer required. msg.topic on Port 0; also fired on this.emitter for sibling modules

The default measurement output (delta-compressed payload from outputUtils.formatMsg) keeps msg.topic = config.general.name per existing convention. evt.* is for additional event-shaped emissions, not the per-tick measurement stream.

Tip

The prefix is the kind, never the target. Don't write pump.set.demand — write set.demand and let routing handle which pump. The prefix system says explicitly what the message does; the target is identified by node id, not by topic.

Caution

One topic, two effects is a bug magnet. A topic like setStartup that both sets a flag and triggers startup should be split into set.<noun> and cmd.<verb>.


Aliases and deprecation

Each commands/index.js declares the canonical name as topic and lists pre-refactor names in aliases. First use of each alias logs a one-time deprecation warning. Aliases are removed in Phase 7 after one release cycle.

{
  topic: 'set.mode',
  aliases: ['setMode', 'changemode'],
  payloadSchema: { type: 'string' },
  description: 'Switch the node between auto and manual control modes.',
  handler: handlers.setMode,
}

Common alias map

Canonical Legacy aliases
set.mode setMode, changemode
set.demand Qd, setDemand
cmd.startup execSequence (with payload.action='startup')
cmd.shutdown execSequence (with payload.action='shutdown')
child.register registerChild
data.pressure pressure
data.flow flow

Important

Update integrations to canonical names before Phase 7 ships. Aliases work today; they will be removed next major release.


Payload schemas

payloadSchema.type accepts six values. Source: .claude/refactor/CONTRACTS.md §4.

Type Meaning
'string' typeof payload === 'string'
'number' typeof payload === 'number'
'boolean' typeof payload === 'boolean'
'object' Non-null object. Optional properties: { key: 'typeName' } enforces per-key typeof (missing keys allowed)
'any' Anything passes. Use when handler accepts heterogeneous payloads
'none' Trigger-only. Handler invoked regardless of payload. If msg.payload is anything but undefined / null, registry logs a warn and still invokes the handler

Unit coercion (pre-dispatch)

A descriptor for a numeric setter or data topic may declare:

{
  topic: 'set.demand',
  units: { measure: 'volumeFlowRate', default: 'm3/h' },
  payloadSchema: { type: 'number' },
  handler: handlers.setDemand,
}
flowchart LR
    in["Inbound msg &mdash; payload=50, unit='m3/h'"]
    parse["Extract value+unit &mdash; 3 payload shapes accepted"]
    convert["convert(value).from(unit).to(default)"]
    handler["Handler receives msg.payload = canonical number, msg.unit = units.default"]

    in --> parse --> convert --> handler

    class in,handler neutral
    class parse,convert step

    classDef neutral fill:#dddddd
    classDef step fill:#a9daee,color:#000

Three accepted payload shapes

Shape Example
Plain number msg.payload = 50; msg.unit = 'l/s'
Object with explicit unit msg.payload = { value: 50, unit: 'l/s' }
Object without unit (falls back to msg.unit) msg.payload = { value: 50 }; msg.unit = 'l/s'

Behaviour on unit mismatch

Situation What the registry does
No unit supplied Silently assume units.default
Unit recognised + correct measure Convert and rewrite payload
Unit recognised, wrong measure Log warn with accepted-unit list; fall through
Unit unrecognised Log warn with accepted-unit list; fall through

The handler always sees a plain number in units.default. Source: .claude/refactor/CONTRACTS.md §4 ("Determine the unit-of-record").


S88 colour palette

Every Mermaid diagram, every Node-RED node editor colour, every FlowFuse dashboard group uses this palette. Source: .claude/rules/node-red-flow-layout.md §14.

Hex S88 level Used by
#0f52a5 Area Reserved — not used yet
#0c99d9 Process Cell pumpingStation
#50a8d9 Unit MGC, VGC, reactor, settler, monster
#86bbdd Equipment Module rotatingMachine, valve, diffuser
#a9daee Control Module measurement
#dddddd Utility / neutral dashboardAPI, helper functions

Warning

Known palette outliers (pending cleanup, tracked in .claude/refactor/OPEN_QUESTIONS.md):

  • settler editor colour is #e4a363 (orange) — should be #50a8d9.
  • monster editor colour is #4f8582 (teal) — should be #50a8d9.
  • diffuser registers under category 'wbd typical' instead of 'EVOLV'.

Wiki diagrams use the correct S88 colour regardless of the editor mismatch.


Measurement key shape

MeasurementContainer stores values under composite keys:

<type>.<variant>.<position>.<childId>
   |       |          |          |
   |       |          |          +-- child id (or 'default' for internal computations)
   |       |          +------------- 'upstream' / 'downstream' / 'atequipment' / ...  (always lowercase in keys)
   |       +------------------------ 'measured' / 'predicted' / 'setpoint' / 'min' / 'max'
   +-------------------------------- 'flow' / 'pressure' / 'power' / 'temperature' / 'level'

Examples

Key Meaning
flow.measured.downstream.dashboard-sim-downstream Externally measured downstream flow
flow.predicted.downstream.default The node's own prediction
power.measured.atequipment.default Measured power at the equipment
pressure.measured.upstream.<childId> Pressure from a specific measurement child

Warning

position is always lowercase in keys. The configuration form may use mixed case (atEquipment); the container normalises. Don't rely on the casing the form shows you.


Status badge

statusBadge.compose(state) returns {fill, shape, text} for node.status(...).

const { statusBadge } = require('generalFunctions');

statusBadge.compose(['OK', `flow=${flow.toFixed(1)} m3/h`])
statusBadge.error(message)
statusBadge.idle(label)
fill shape Meaning
blue dot Normal / running
green dot Success / running optimally
yellow ring Degraded — attention needed
red ring Fault — operator action required
grey dot Initialising / no data yet

Important

Badges live in the domain, not the adapter. nodeClass calls this.source.getStatusBadge() once per second; the domain owns the shape. Source: .claude/refactor/CONTRACTS.md §7.


HealthStatus

A standardised shape for nodes that compute prediction quality, drift, or general health. Source: .claude/refactor/CONTRACTS.md §9.

{
  "level": 1,
  "flags": ["pressure_init_warming"],
  "message": "warmup phase",
  "source": "rotatingMachine#pump-A"
}
Field Type Meaning
level 0 | 1 | 2 | 3 0 = fine, 3 = unusable
flags string[] Machine-readable tags (e.g. no_pressure_input)
message string Single-line human summary
source string | null <nodeType>#<id> — for routing UI / alarm correlation

Helpers compose multiple sub-statuses (flow drift + power drift + pressure init) into one node-level status.


Canonical units (used in code)

Every node declares its UnitPolicy: what canonical (internal) unit it uses and what output unit to render to. Source: .claude/refactor/CONTRACTS.md §6.

Quantity Canonical (internal) Common output
Flow m3/s m3/h, l/s, gpm
Pressure Pa bar, mbar, kPa
Power W kW, MW
Temperature K degC, degF
Level m m, cm
Volume m3 m3, l

Tip

Inside specificClass, treat values as canonical. Conversion happens at the boundary: input coercion by the commands registry; output formatting by outputUtils.

Dual access form

UnitPolicy exposes each accessor as both a method and a frozen property bag.

policy.canonical('flow')   // 'm3/s'   (method form)
policy.canonical.flow      // 'm3/s'   (property form &mdash; preferred in hot paths)
policy.output.pressure     // 'mbar'

Page Why
Architecture Where these conventions are implemented in code
Telemetry What these keys look like in InfluxDB
Topology Patterns Which topics flow between which nodes
Glossary Domain terms used here