PALETTE REDESIGN (2026-05-21)
Sidebar swatches switched from S88 level (all blue) to domain-hue per node.
Family hue = function (rotating=orange, valves=teal, biology=green/olive,
sampling=violet, sensor=amber, aeration=sky-blue, infrastructure=slate);
within a family, darker = higher S88 / "more controller-ish."
Editor-group rectangles in flow.json still follow S88 — only the
registerType colour changed.
Submodule bumps for palette: rotatingMachine, machineGroupControl,
pumpingStation, valve, valveGroupControl, reactor, settler, monster,
measurement, diffuser, dashboardAPI.
Docs touched:
- CLAUDE.md: palette swatch vs. editor-group bullets split out.
- .claude/rules/node-red-flow-layout.md: new §10.0 introduces the two
color systems, full 12-row palette table, and explicit warning not to
mix the two hexes.
- .claude/refactor/MODULE_SPLIT.md: per-node headers annotated with
both `group #XXX` and `palette #XXX`.
- .claude/refactor/WIKI_HOME_TEMPLATE.md + WIKI_TEMPLATE.md: clarify
Mermaid classDefs visualize hierarchy, not palette swatches.
- .claude/refactor/OPEN_QUESTIONS.md: dated decision entry with
rationale, file list, and follow-ups.
CORESYNC SUBMODULE (new)
nodes/coresync added pointing at https://gitea.wbd-rd.nl/RnD/coresync.
FROST/SensorThings handoff path — first version forwards FROST-ready HTTP
request messages on the dbase output; a downstream http-request node
performs the POST and feeds responses back on msg.topic = "frost.response".
Lazy stream resolver, latest-wins queue (keep first + latest, drop middle),
knot-emit on slope change, provenance preserved in Observation parameters.
- .gitmodules: add nodes/coresync entry.
- package.json: register coresync as a Node-RED node.
- generalFunctions bump: new frostFormatter + 4 node config schemas
expose the dbase format option.
- measurement bump: "frost" option added to dbaseOutputFormat dropdown
(plus the in-flight data.measurement unit-handling work).
- machineGroupControl bump: small editor compact-fields tweak alongside
the palette change.
- CORESYNC_FROST_INTERVIEW_HANDOFF.md added at root with interview state
(Q20 open: slope angle vs. relative delta comparison).
DASHBOARDAPI MODULE_NOT_FOUND FIX
package.json: dashboardapi entry path corrected to
nodes/dashboardAPI/dashboardAPI.js. Commit e04c4a1 renamed the files to
camelCase but missed package.json; on case-sensitive filesystems
(Linux/Docker, where the tarball lands) the require resolved to nothing
and the node showed MODULE_NOT_FOUND in the Node-RED palette.
MISC CLEANUP
- examples/README.md + examples/pumpingstation-complete-example/ removal
(build_flow.py, flow.json, README.md superseded by per-node examples).
- jest.config.js: in-progress tweak.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.2 KiB
EVOLV - Claude Code Project Guide
READ FIRST, BEFORE ANY OTHER WORK:
CONTRACTS.md— front-door map: where every contract, rule, and standard lives, and how to find them.
What This Is
Node-RED custom nodes package for wastewater treatment plant automation. Developed by Waterschap Brabantse Delta R&D team. Follows ISA-88 (S88) batch control standard.
Architecture
Each node follows a three-layer pattern:
- Node-RED wrapper (
<nodeName>.js) - registers the node type, sets up HTTP endpoints - Node adapter (
src/nodeClass.js) - bridges Node-RED API with domain logic, handles config loading, tick loops, events - Domain logic (
src/specificClass.js) - pure business logic, no Node-RED dependencies
Folder & File Layout (READ BEFORE CREATING NEW FILES)
Every per-node file MUST use the folder name exactly (case-sensitive). No abbreviations. Quick reference:
| Path | Required name |
|---|---|
| Entry file | nodes/<nodeName>/<nodeName>.js |
| Editor HTML | nodes/<nodeName>/<nodeName>.html |
| Node adapter | nodes/<nodeName>/src/nodeClass.js |
| Domain logic | nodes/<nodeName>/src/specificClass.js |
| Editor JS modules | nodes/<nodeName>/src/editor/*.js (extract when inline editor JS exceeds ~50 lines) |
| Tests | nodes/<nodeName>/test/{basic,integration,edge}/*.test.js |
| Example flows | nodes/<nodeName>/examples/*.flow.json |
Full rule + serving recipe for src/editor/: .claude/rules/node-architecture.md.
Legacy drift to rename when the file is next touched (do not introduce new
mismatches in the meantime). When renaming, keep the Node-RED type id
lowercase (registerType('mgc', …) etc.) so deployed flows continue to load —
only the file paths change. dashboardAPI was migrated this way on 2026-05-19.
| Node | Currently | Should be |
|---|---|---|
machineGroupControl |
mgc.{js,html} |
machineGroupControl.{js,html} |
valveGroupControl |
vgc.{js,html} |
valveGroupControl.{js,html} |
Key Shared Library: nodes/generalFunctions/
logger- structured logging (use this, NOT console.log)MeasurementContainer- chainable measurement storage (type/variant/position)configManager- loads JSON configs fromsrc/configs/MenuManager- dynamic UI dropdownsoutputUtils- formats messages for InfluxDB and process outputschildRegistrationUtils- parent-child node relationshipscoolprop- thermodynamic property calculations
Conventions
- Nodes register under category
'EVOLV'in Node-RED - Two color systems (don't confuse):
- Palette swatch (Node-RED sidebar, set in
<node>.html) = domain-hue per node — full table in.claude/rules/node-red-flow-layout.md§10.0. Changed 2026-05-21; see.claude/refactor/OPEN_QUESTIONS.md. - Editor-group rectangle (flow.json
style.fill) = S88 level (unchanged): Area=#0f52a5, ProcessCell=#0c99d9, Unit=#50a8d9, Equipment=#86bbdd, ControlModule=#a9daee
- Palette swatch (Node-RED sidebar, set in
- Config JSON files in
generalFunctions/src/configs/define defaults, types, enums per node - Tick loop is opt-in per node — default cadence 1000 ms, but each node sets
static tickInterval(or skips it). See.claude/refactor/OPEN_QUESTIONS.md(2026-05-10 entry) for the design decision - Output ports + 3-tier architecture + file-naming +
src/editor/layout: see.claude/rules/node-architecture.md - Multi-tab demo flows: see
.claude/rules/node-red-flow-layout.mdfor the tab/link-channel/spacing rule set used byexamples/ - Output coverage (every output, every state, every layer): see
.claude/rules/output-coverage.md— manifest + populated/degraded tests are mandatory for any change that touches Port 0/1/2 keys, function-node fan-outs, telemetry fields, or dashboard widget sources
Agents and Skills (use them — don't reinvent)
- Skills at
.claude/skills/evolv-*/SKILL.md(15 domain skills) — auto-discovered, invoke via theSkilltool. Load them when you need domain reasoning (rotating equipment, biology, telemetry, security, instrumentation, hydraulics, alarms, OT integration, regulatory, quality, commissioning, frontend, …). - Subagents at
.claude/agents/*.md(10 Claude Code subagents) — spawnable viaAgent(subagent_type: '<name>'). Use for independent work:evolv-orchestrator(multi-domain decomposition +teamworkflows),mechanical-process-engineer,biological-process-engineer,instrumentation-measurement,node-red-runtime,telemetry-database,quality-test-engineer,commissioning-compliance,ot-security-integration,general-functions-library. - Routing table:
.agents/AGENTS.mdmaps task patterns → which skill/subagent to invoke. teamkeyword: when the user says "team", spawnevolv-orchestrator(subagent) — it picks specialists, runs an alignment pass, returns one integrated answer.
Tooling (Docker-first, local now, central later)
Custom EVOLV tooling lives in tools/ and is intended to run inside the local Docker compose stack (tools/docker-compose.yml). Always prefer these tools over ad-hoc grep/curl/manual checks — they encode the rules in .claude/rules/ and catch regressions the human review would miss:
tools/flow-lint/— validatesexamples/*.flow.jsonagainst.claude/rules/node-red-flow-layout.md. Run before committing any flow change.tools/output-manifest-verify/— diffs declared Port 0/1/2 keys vs. runtime emissions. Run on any output-shape change.tools/contract-verify/— diffsnodes/<n>/CONTRACT.mdvs.src/commands/index.js. Run after touching a command registry.tools/wiki-gen/— regenerates topic-contract + data-model sections ofnodes/<n>/wiki/. Run after a CONTRACT change.tools/physics-sanity/— cross-node mass/hydraulic/energy balance assertions. Run as part ofnode --testfor cross-node changes.- MCP services (Node-RED admin, InfluxDB, headless browser) live under
tools/mcp/as Docker services. Migration note: these will move to a central MCP server later; the local stack is interim. The Dockerfile + compose entry stays in this repo as the canonical definition. - Why use them: every tool encodes a rule that we've previously discovered through a bug (η-null crash, ui-chart blank renders, output-key drift). Skipping them re-opens those bugs.
Sources of truth (the canonical files)
- Front-door map:
CONTRACTS.md— read first; lists every standard and where it lives - Platform API shapes (BaseDomain, BaseNodeAdapter, commands registry, UnitPolicy, …):
.claude/refactor/CONTRACTS.md - Code conventions (file/function size, comments, naming):
.claude/refactor/CONVENTIONS.md - Per-node module layout:
.claude/refactor/MODULE_SPLIT.md - Per-node API contract:
nodes/<n>/CONTRACT.md+nodes/<n>/src/commands/index.js(source of truth for acceptedmsg.topicvalues) - Shared library API:
nodes/generalFunctions/CONTRACT.md(exported classes + utilities) - Live decisions log:
.claude/refactor/OPEN_QUESTIONS.md— append, don't invent
Development Notes
- No build step required - pure Node.js
- Install:
npm installin root - Submodule URLs were rewritten from
gitea.centraal.wbd-rd.nltogitea.wbd-rd.nlfor external access - Dependencies: mathjs, generalFunctions (git submodule)