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>
This commit is contained in:
znetsixe
2026-05-11 22:24:51 +02:00
parent 2ccc8aea9e
commit 5ae8788fd7
33 changed files with 1491 additions and 729 deletions

View File

@@ -1,80 +1,265 @@
# Topic Conventions
> **Reflects code as of `9ab9f6b` · regenerated `2026-05-11`**
![code-ref](https://img.shields.io/badge/code--ref-9ab9f6b-blue)
![source](https://img.shields.io/badge/source-CONTRACTS.md_%C2%A71-orange)
Naming rules, unit policy, and S88 colour palette. Source of truth: `.claude/refactor/CONTRACTS.md` §1.
> [!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.
## Topic prefixes
---
Every topic is `<prefix>.<verb>` lowercase. Five prefixes only.
## The six prefixes
```mermaid
flowchart LR
ui[UI / parent / driver]:::neutral
node[Node]:::tier3
child[Child]:::tier1
ui["UI / parent / driver / function node"]
node[Node]
child["Child node"]
ext["external consumers"]
ui -->|set.x / cmd.x| node
node -->|evt.x| ui
child -->|data.x| node
node -->|data.x| child
child -->|child.register| node
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
```
| Prefix | Direction | Semantics | Examples |
|---|---|---|---|
| `set.` | inbound | Set a configurable value. **Idempotent**, no side-effects beyond storing the value. | `set.mode`, `set.demand`, `set.position` |
| `cmd.` | inbound | Trigger an action. **Has side-effects** (state transitions, motor commands). | `cmd.startup`, `cmd.shutdown`, `cmd.calibrate`, `cmd.estop` |
| `data.` | bidirectional | Carries measurement / process data. Used by `measurement → parent` and emitters. | `data.pressure`, `data.flow`, `data.temperature` |
| `evt.` | outbound | Announces something happened. Consumer-driven. | `evt.state-change`, `evt.alarm`, `evt.health` |
| `child.` | inbound (parent) | Child node lifecycle. | `child.register` (with legacy alias `registerChild`) |
### Inbound (the node accepts on its input)
**Anti-patterns to avoid:**
- ❌ A topic that does two things (`setStartup` to both set a flag *and* trigger startup). Split into `set.` + `cmd.`.
- ❌ Reusing a `cmd.` topic for both inbound trigger and outbound ack — make a paired `evt.<verb>-complete`.
- ❌ Per-node prefixes (`pump.set.demand`). The prefix is the *kind*, not the *target*.
| 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` |
## Alias deprecation
### Outbound (the node emits)
Legacy topic names (pre-refactor) are still accepted as aliases. The current alias map per node lives in `src/commands/index.js`. Common aliases:
| Prefix | Meaning | Where it appears |
|:---|:---|:---|
| `evt.<noun>` | Event. A fact about something that just happened. Fire-and-forget &mdash; 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` &mdash; 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.
```js
{
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` |
|:---|:---|
| `set.mode` | `setMode`, `changemode` |
| `set.demand` | `Qd`, `setDemand` |
| `cmd.startup` | `execSequence` with `payload.action='startup'` |
| `cmd.shutdown` | `execSequence` with `payload.action='shutdown'` |
| `cmd.startup` | `execSequence` (with `payload.action='startup'`) |
| `cmd.shutdown` | `execSequence` (with `payload.action='shutdown'`) |
| `child.register` | `registerChild` |
| `data.pressure` | `pressure` |
| `data.flow` | `flow` |
Aliases are logged at debug level on use. Plan is to remove them in a future major version. Update integrations to canonical names.
> [!IMPORTANT]
> Update integrations to canonical names before Phase 7 ships. Aliases work today; they will be removed next major release.
## Unit policy
---
Every node declares canonical + output units via `UnitPolicy.declare({canonical, output})`. The command registry coerces incoming `msg.unit` to the canonical unit before the handler runs. Outputs are emitted in the declared output unit (often human-friendly).
## 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:
```js
{
topic: 'set.demand',
units: { measure: 'volumeFlowRate', default: 'm3/h' },
payloadSchema: { type: 'number' },
handler: handlers.setDemand,
}
```
```mermaid
flowchart LR
ui[UI message<br/>e.g. 50 m³/h]:::neutral
coerce[unit coercion<br/>m³/h → m³/s]:::tier1
sc[specificClass<br/>canonical m³/s]:::tier3
out[output<br/>renders back to m³/h]:::tier2
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"]
ui --> coerce --> sc --> out
in --> parse --> convert --> handler
class in,handler neutral
class parse,convert step
classDef neutral fill:#dddddd
classDef tier1 fill:#a9daee,color:#000
classDef tier3 fill:#50a8d9,color:#000
classDef tier2 fill:#86bbdd,color:#000
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 &mdash; 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) &mdash; should be `#50a8d9`.
> - `monster` editor colour is `#4f8582` (teal) &mdash; 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(...)`.
```js
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 &mdash; attention needed |
| `red` | `ring` | Fault &mdash; 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.
```json
{
"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>` &mdash; 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` |
@@ -82,104 +267,26 @@ flowchart LR
| Level | `m` | `m`, `cm` |
| Volume | `m3` | `m3`, `l` |
**Rule:** anywhere in `specificClass`, treat values as canonical. Conversion happens at the boundary (input coercion + output formatting).
> [!TIP]
> Inside `specificClass`, treat values as canonical. Conversion happens at the boundary: input coercion by the commands registry; output formatting by `outputUtils`.
## S88 colour palette
### Dual access form
```mermaid
flowchart TB
A[Area<br/>#0f52a5]:::area
PC[Process Cell<br/>#0c99d9]:::pc
UN[Unit<br/>#50a8d9]:::unit
EM[Equipment Module<br/>#86bbdd]:::equip
CM[Control Module<br/>#a9daee]:::ctrl
UT[Utility / neutral<br/>#dddddd]:::neutral
`UnitPolicy` exposes each accessor as both a method and a frozen property bag.
A --> PC --> UN --> EM --> CM
UT -.- A
classDef area fill:#0f52a5,color:#fff
classDef pc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
classDef neutral fill:#dddddd,color:#000
```js
policy.canonical('flow') // 'm3/s' (method form)
policy.canonical.flow // 'm3/s' (property form &mdash; preferred in hot paths)
policy.output.pressure // 'mbar'
```
| Hex | S88 level | Used by |
|---|---|---|
| `#0f52a5` | Area | (reserved — not in use yet) |
| `#0c99d9` | Process Cell | pumpingStation |
| `#50a8d9` | Unit | machineGroupControl, valveGroupControl, reactor, settler, monster |
| `#86bbdd` | Equipment Module | rotatingMachine, valve, diffuser |
| `#a9daee` | Control Module | measurement |
| `#dddddd` | Utility / neutral | dashboardAPI, helper function nodes |
**Rule:** every Mermaid diagram in this wiki, every Node-RED node's editor colour, and every dashboard grouping uses this palette. Source of truth: `.claude/rules/node-red-flow-layout.md` §14.
**Known outliers** (pending cleanup, tracked in OPEN_QUESTIONS.md):
- `settler` editor colour is `#e4a363` (orange) — should be `#50a8d9`.
- `monster` editor colour is `#4f8582` (teal) — should be `#50a8d9`.
- `diffuser` editor colour was missing pre-refactor; now `#86bbdd`.
- `dashboardAPI` registers under category `'wbd typical'` instead of `'EVOLV'`.
## Measurement key shape
The `MeasurementContainer` stores values under composite keys:
```
<type>.<variant>.<position>.<childId>
```
| Segment | Examples |
|---|---|
| `type` | `flow`, `pressure`, `power`, `temperature`, `level` |
| `variant` | `measured`, `predicted`, `setpoint`, `min`, `max` |
| `position` | `upstream`, `downstream`, `atequipment`, `inlet`, `outlet` (always lowercase in keys) |
| `childId` | The registering child's id, OR `default` for internal computations |
Examples:
- `flow.measured.downstream.dashboard-sim-downstream` — externally measured downstream flow.
- `flow.predicted.downstream.default` — node's own prediction.
- `power.measured.atequipment.default` — measured power at the equipment.
- `pressure.measured.upstream.<childId>` — pressure from a specific measurement child.
**Gotcha:** `position` is **always lowercase in keys**. The configuration form may use mixed case (`atEquipment`); the container normalises.
## Status badge
`statusBadge.compose(state)` returns `{fill, shape, text}` for `node.status(...)`:
| level | shape | fill | meaning |
|---|---|---|---|
| `info` | dot | blue | normal operation |
| `success` | dot | green | success / running optimally |
| `warning` | ring | yellow | degraded, attention needed |
| `error` | ring | red | fault, operator action required |
| `pending` | dot | grey | initialising / no data yet |
Composer reads from `HealthStatus.level` (03) — kept centralised so all nodes show consistent badges.
## HealthStatus shape
```json
{
"level": 0,
"flags": ["pressure_init_warming"],
"message": "warmup phase",
"source": "rotatingMachine#pump-A"
}
```
| Field | Range | Meaning |
|---|---|---|
| `level` | 0..3 | 0 = healthy, 1 = degraded, 2 = warning, 3 = error |
| `flags` | string[] | Machine-readable reason codes. |
| `message` | string | Human-readable summary (one line). |
| `source` | string | `<nodeType>#<id>` — for routing UI / alarm correlation. |
---
## Related pages
- [Architecture](Architecture) — generalFunctions API surface
- [Telemetry](Telemetry) — Port-1 InfluxDB schema (where these conventions appear in stored data)
- [Topology-Patterns](Topology-Patterns) — what topics flow where
| Page | Why |
|:---|:---|
| [Architecture](Architecture) | Where these conventions are implemented in code |
| [Telemetry](Telemetry) | What these keys look like in InfluxDB |
| [Topology Patterns](Topology-Patterns) | Which topics flow between which nodes |
| [Glossary](Glossary) | Domain terms used here |