Files
EVOLV/CONTRACTS.md
znetsixe 5e2c01ece3 docs + submodules: final backlog clearance — valve/reactor/dashboardAPI
Superproject:
- CLAUDE.md: legacy-drift table loses the dashboardAPI row (migrated);
  drift section notes the type-id-preservation strategy for the
  remaining mgc / vgc renames.
- CONTRACTS.md: canonical-unit rule explicitly carves out reactor as
  an approved ASM-textbook exception with the conversion boundary.

Submodules:
- nodes/valve @ 167b102: CONTRACT documents valve's lack of an FSM
  maintenance state (schema mode enum accepts `maintenance` but no
  enter/exit sequences exist). Limits made explicit instead of being
  hidden as a wiki TODO.
- nodes/reactor @ 75d0413: CONTRACT now declares the approved ASM-unit
  divergence (mg/L, m³/d, °C, 1/h) with the conversion boundary spelled
  out. Closes the canonical-unit drift surfaced by the wiki audit.
- nodes/dashboardAPI @ ......: file rename (preserves type id).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 16:37:05 +02:00

149 lines
8.9 KiB
Markdown

# EVOLV — Contracts, Rules, and Standards
> **Front door for humans and agents working in this repo.**
> If you only read one file before touching code, read this one. It maps every
> contract, rule, and standard in the EVOLV stack and tells you where each
> lives. Everything else here is a link to a more specific file.
EVOLV is a Node-RED node library for wastewater treatment plant automation,
built by Waterschap Brabantse Delta R&D. All work happens on the `development`
branch across 12 submodules; promotion to `main` is gated by Docker E2E.
---
## 1. Where everything lives
### Platform-wide (EVOLV root)
| What | Where | Read when |
|---|---|---|
| **This map** | `CONTRACTS.md` (this file) | First time, or when orienting |
| **Agent entry-point instructions** | [`CLAUDE.md`](./CLAUDE.md) | Auto-loaded by Claude Code |
| **Active rules** | [`.claude/rules/`](./.claude/rules/) (7 files) | Triggered by `paths:` frontmatter or referenced from `CLAUDE.md` |
| **Platform API contracts** | [`.claude/refactor/CONTRACTS.md`](./.claude/refactor/CONTRACTS.md) | Before changing `generalFunctions` exports or any base class |
| **Code conventions** | [`.claude/refactor/CONVENTIONS.md`](./.claude/refactor/CONVENTIONS.md) | Before writing or editing any file |
| **Per-node concern layout** | [`.claude/refactor/MODULE_SPLIT.md`](./.claude/refactor/MODULE_SPLIT.md) | When adding files to `nodes/<n>/src/` |
| **Wiki page templates** | [`.claude/refactor/WIKI_TEMPLATE.md`](./.claude/refactor/WIKI_TEMPLATE.md) + [`WIKI_HOME_TEMPLATE.md`](./.claude/refactor/WIKI_HOME_TEMPLATE.md) | When editing a per-node wiki page |
| **Live decisions log** | [`.claude/refactor/OPEN_QUESTIONS.md`](./.claude/refactor/OPEN_QUESTIONS.md) | When you spot an ambiguity — append, don't invent |
| **Top-level wiki** | [`wiki/`](./wiki/) (Home, Architecture, Getting-Started, Telemetry, Topology-Patterns, Topic-Conventions, Glossary, Functional-Overview) | When you need a process-level or architecture-level view |
| **Agent skills** | [`.claude/skills/`](./.claude/skills/) (15 domain skills, auto-discovered, invokable via `Skill` tool) | When you need domain reasoning |
| **Spawnable subagents** | [`.claude/agents/`](./.claude/agents/) (10 Claude Code subagents) | When you want to delegate independent work |
| **Routing table** | [`.agents/AGENTS.md`](./.agents/AGENTS.md) | When deciding which specialist to invoke |
| **Improvements backlog** | [`.agents/improvements/IMPROVEMENTS_BACKLOG.md`](./.agents/improvements/IMPROVEMENTS_BACKLOG.md) | When deferring functional work |
### Per-node (`nodes/<nodeName>/`)
| What | Where | Read when |
|---|---|---|
| Node entry instructions for agents | `nodes/<n>/CLAUDE.md` | Auto-loaded when touching files in that subtree |
| **Node API contract** | `nodes/<n>/CONTRACT.md` | Before changing `msg.topic` inputs/outputs/events |
| **Command registry (source of truth)** | `nodes/<n>/src/commands/index.js` | When adding/removing an accepted topic |
| **Domain logic** | `nodes/<n>/src/specificClass.js` | Pure JS; no `RED.*` allowed |
| **Node-RED adapter** | `nodes/<n>/src/nodeClass.js` | Bridge to runtime; ≤ 25 lines, extends `BaseNodeAdapter` |
| **Per-node wiki** | `nodes/<n>/wiki/``Home.md`, `Reference-{Architecture,Contracts,Limitations,Examples}.md` | Topic-contract + data-model sections autogen via `npm run wiki:all` |
| **Tests** | `nodes/<n>/test/{basic,integration,edge}/` | Required for every change |
| **Example flows** | `nodes/<n>/examples/{basic,integration,edge}.flow.json` | Required artifact per node |
### Shared library (`nodes/generalFunctions/`)
| What | Where |
|---|---|
| **Library API contract** | `nodes/generalFunctions/CONTRACT.md` |
| **Public exports** | `nodes/generalFunctions/index.js` (barrel) |
| **Source** | `nodes/generalFunctions/src/{domain,nodered,measurements,convert,configs,…}/` |
### Archives (don't take as authoritative)
| What | Where | Why kept |
|---|---|---|
| Pre-refactor wiki pages | [`wiki/Archive/`](./wiki/Archive/) (20 files) | Historical reference; each has `⚠️ ARCHIVED — Do not update` |
| Refactor plan artifacts | [`.claude/refactor/Archive/`](./.claude/refactor/Archive/) — `CONTINUE_HERE.md`, `TASKS.md` | The May-2026 refactor plan; phases all done |
| Old priority lists | [`.agents/improvements/Archive/`](./.agents/improvements/Archive/) | Pre-refactor production priorities |
---
## 2. Discovery chain — how a fresh agent finds the rules
1. `CLAUDE.md` auto-loads → points at this file.
2. `.claude/rules/*.md` auto-load by `paths:` frontmatter when editing matching files.
3. `nodes/<n>/CLAUDE.md` auto-loads when working under that submodule.
4. This file (`CONTRACTS.md`) is the human-facing map of everything in step 1-3.
5. **Concept lookup**: use `grep` / `find` or the `Explore` subagent — anchor on the canonical sources listed in §1 (commands registry, CONTRACT.md, base classes in `generalFunctions/`).
---
## 3. The three contracts every node honours
Every EVOLV node is a three-tier sandwich. Each tier has a contract:
| Tier | Class | Contract source | Per-node implementation |
|---|---|---|---|
| 1 — Entry | `RED.nodes.registerType` | [`.claude/rules/node-architecture.md`](./.claude/rules/node-architecture.md) | `nodes/<n>/<n>.js` |
| 2 — Adapter | `BaseNodeAdapter` (from `generalFunctions`) | [`.claude/refactor/CONTRACTS.md §2`](./.claude/refactor/CONTRACTS.md) | `nodes/<n>/src/nodeClass.js` |
| 3 — Domain | `BaseDomain` (from `generalFunctions`) | [`.claude/refactor/CONTRACTS.md §3`](./.claude/refactor/CONTRACTS.md) | `nodes/<n>/src/specificClass.js` |
Plus the **commands registry** (`nodes/<n>/src/commands/index.js`) declares
the `msg.topic` inputs; `BaseNodeAdapter` dispatches by topic lookup. See
[`.claude/refactor/CONTRACTS.md §4`](./.claude/refactor/CONTRACTS.md).
---
## 4. Output and telemetry contract
Three output ports per node. Source of truth: [`.claude/refactor/CONTRACTS.md §10`](./.claude/refactor/CONTRACTS.md) and [`wiki/Telemetry.md`](./wiki/Telemetry.md).
| Port | Carries | Formatter |
|---|---|---|
| 0 | Process data (delta-compressed) | `outputUtils.formatMsg(..., 'process')` |
| 1 | InfluxDB line protocol | `outputUtils.formatMsg(..., 'influxdb')` |
| 2 | Registration / control plumbing | hand-shaped on the parent-child handshake |
Output-coverage testing (manifest + populated + degraded states) is **mandatory**
for any change touching Port 0/1/2 keys, function-node fan-outs, or dashboard widgets.
See [`.claude/rules/output-coverage.md`](./.claude/rules/output-coverage.md).
---
## 5. When a contract changes — the rule
1. Update the source file (`src/commands/index.js`, `src/specificClass.js`, or `generalFunctions/index.js`).
2. Update the per-node `CONTRACT.md` (Inputs table is partially autogenerated; the rest is hand-maintained).
3. Run `npm run wiki:all` inside the submodule to regenerate the topic-contract + data-model sections in `wiki/`.
4. If the change touched a platform shape (a base class or shared utility), update [`.claude/refactor/CONTRACTS.md`](./.claude/refactor/CONTRACTS.md) and `nodes/generalFunctions/CONTRACT.md`.
5. If the change introduced a deprecation, add an alias to `commands/index.js` and a one-line note to the per-node `CONTRACT.md`.
6. Append unresolved questions to [`.claude/refactor/OPEN_QUESTIONS.md`](./.claude/refactor/OPEN_QUESTIONS.md). Don't invent answers.
7. If topic usage in an example flow changed, regenerate or review the per-node `wiki/Reference-Examples.md` and the `examples/*.flow.json` set.
---
## 6. Conventions in one paragraph (the rest is in `CONVENTIONS.md`)
Files ≤ 200 lines (300 hard cap); functions ≤ 30 lines (60 hard cap). Default
to no comments — add one only when *why* is non-obvious. `specificClass`
**never** imports `RED.*`. Logger from `generalFunctions`, never `console.log`.
S88 colour scheme is mandatory in diagrams. Topic prefixes: `set.<noun>` for
idempotent setters, `cmd.<verb>` for triggers, `evt.<noun>` for events.
Tests live in `test/{basic,integration,edge}/`. Submodule commits go in the
submodule first, then the superproject bumps the pin.
**Canonical units** (Pa / m³/s / W / K) apply to every node **except
`reactor`**, which deliberately uses ASM-kinetics literature units
(mg/L, m³/d, °C, 1/h) — documented in `nodes/reactor/CONTRACT.md`.
Conversions happen at the parent/child boundary via `UnitPolicy`.
---
## 7. Verification checklist before merge
- [ ] Per-node tests green (`cd nodes/<n> && node --test test/basic test/integration test/edge`).
- [ ] `CONTRACT.md` updated for any added / removed / renamed topic, port-0 key, or event.
- [ ] `npm run wiki:all` re-run in the touched submodule(s).
- [ ] Output-coverage manifest + tests updated if any output shape changed.
- [ ] Submodule pin bumped in the superproject.
- [ ] Commit message captures *why* — load-bearing decisions go in the commit body and PR description.
---
*Last reviewed: 2026-05-19. If something in this map is wrong, fix this file
in the same PR as the change that made it wrong.*