# EVOLV - Claude Code Project Guide > **READ FIRST, BEFORE ANY OTHER WORK:** > [`CONTRACTS.md`](./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: 1. **Node-RED wrapper** (`.js`) - registers the node type, sets up HTTP endpoints 2. **Node adapter** (`src/nodeClass.js`) - bridges Node-RED API with domain logic, handles config loading, tick loops, events 3. **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//.js` | | Editor HTML | `nodes//.html` | | Node adapter | `nodes//src/nodeClass.js` | | Domain logic | `nodes//src/specificClass.js` | | Editor JS modules | `nodes//src/editor/*.js` (extract when inline editor JS exceeds ~50 lines) | | Tests | `nodes//test/{basic,integration,edge}/*.test.js` | | Example flows | `nodes//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 from `src/configs/` - `MenuManager` - dynamic UI dropdowns - `outputUtils` - formats messages for InfluxDB and process outputs - `childRegistrationUtils` - parent-child node relationships - `coolprop` - 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 `.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 - 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.md` for the tab/link-channel/spacing rule set used by `examples/` - **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 the `Skill` tool. 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 via `Agent(subagent_type: '')`. Use for independent work: `evolv-orchestrator` (multi-domain decomposition + `team` workflows), `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.md`](./.agents/AGENTS.md) maps task patterns → which skill/subagent to invoke. - **`team` keyword**: when the user says "team", spawn `evolv-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/` — validates `examples/*.flow.json` against `.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/` — diffs `nodes//CONTRACT.md` vs. `src/commands/index.js`. Run after touching a command registry. - `tools/wiki-gen/` — regenerates topic-contract + data-model sections of `nodes//wiki/`. Run after a CONTRACT change. - `tools/physics-sanity/` — cross-node mass/hydraulic/energy balance assertions. Run as part of `node --test` for 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`](./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//CONTRACT.md` + `nodes//src/commands/index.js` (source of truth for accepted `msg.topic` values) - **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 install` in root - Submodule URLs were rewritten from `gitea.centraal.wbd-rd.nl` to `gitea.wbd-rd.nl` for external access - Dependencies: mathjs, generalFunctions (git submodule)