docs: standards cleanup — single front-door CONTRACTS.md + archive stale plan artifacts

Establish CONTRACTS.md at the EVOLV root as the canonical map of where every
contract, rule, and standard lives. Surface it from CLAUDE.md so every fresh
agent or colleague lands there first.

Reshape .claude/refactor/ to reflect that the platform refactor is done:
live standards stay at the top level; the plan artifacts (CONTINUE_HERE.md,
TASKS.md) move into Archive/ with WARNING banners.

Drop content that drifted out of date or duplicated the new standards stack:
- docs/DEVELOPER_GUIDE.md (pre-refactor walkthrough; superseded by
  wiki/Architecture, wiki/Getting-Started, .claude/rules/node-architecture,
  .claude/refactor/MODULE_SPLIT + per-node CONTRACT.md + src/commands/).
- .agents/decisions/ (15 DECISION files): load-bearing decisions belong in
  commit messages and PR descriptions; live open items in OPEN_QUESTIONS.md.
- .agents/improvements/TOP10_*.md: moved to Archive/.

Bump generalFunctions to 49c77f2 — adds CONTRACT.md inside the library:
different shape from per-node CONTRACT.md files (library API, not msg.topic),
with stability tags and pointers to .claude/refactor/CONTRACTS.md §N.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene De Ren
2026-05-18 15:48:46 +02:00
parent 560cc2f39a
commit 253ac93896
26 changed files with 210 additions and 1278 deletions

View File

@@ -0,0 +1,299 @@
# Task list — ARCHIVED
> [!WARNING]
> **ARCHIVED — Phases 111 landed on `development` in May 2026.**
> This file is the original phased plan and is retained for history. For
> deferred / open work, see [`../OPEN_QUESTIONS.md`](../OPEN_QUESTIONS.md).
> For current standards, start at [`../../../CONTRACTS.md`](../../../CONTRACTS.md) (EVOLV root).
Phased and ordered. The TaskCreate tracker mirrors this list and is the
active, mutable view; this file is the durable plan.
A task is **done** when:
- The code matches the contracts in `CONTRACTS.md`.
- All the affected node's tests are green (`node --test test/basic
test/integration test/edge`).
- A short note is appended in the task tracker if anything was deferred
to `OPEN_QUESTIONS.md`.
## Phase 1 — `generalFunctions` additive infra
Goal: add the new platform pieces. Nothing is removed; nothing existing
changes shape. All existing nodes continue to work unchanged.
| # | Task | Notes |
|---|---|---|
| 1.1 | Add `src/domain/UnitPolicy.js` + tests | Extracted from `rotatingMachine._buildUnitPolicy`. |
| 1.2 | Add `src/domain/ChildRouter.js` + tests | Built on existing `childRegistrationUtils`. |
| 1.3 | Add `src/domain/LatestWinsGate.js` + tests | Extracted from MGC `_dispatchInFlight`/`_delayedCall`. |
| 1.4 | Add `src/domain/HealthStatus.js` + tests | Standardise the `{level, flags, message, source}` shape. |
| 1.5 | Add `src/domain/BaseDomain.js` + tests | Constructor boilerplate; calls subclass `configure()`/`_init()`. |
| 1.6 | Add `src/nodered/commandRegistry.js` + tests | Topic dispatch + alias warnings. |
| 1.7 | Add `src/nodered/statusBadge.js` + tests | `compose`, `error`, `idle`, `byState` helpers. |
| 1.8 | Add `src/nodered/statusUpdater.js` + tests | 1 Hz poller calling `source.getStatusBadge()`. |
| 1.9 | Add `src/nodered/BaseNodeAdapter.js` + tests | The thing every nodeClass extends. |
| 1.10 | Add `src/stats/index.js` + tests | Promote mean/stdDev/median/mad/lerp from `measurement`. |
| 1.11 | Update `generalFunctions/index.js` (additive) | New exports under existing pattern. |
| 1.12 | Run all 12 nodes' tests against the bumped `generalFunctions` | Sanity gate before phase 2. |
Phase-1 commit cadence: one commit per task on the `development` branch
of `generalFunctions`. Submodule pointer in parent EVOLV bumps **once**
at end of phase.
## Phase 2 — pumpingStation pilot
Goal: prove the new infrastructure end-to-end. Pumping station is a
mid-complexity node — bigger than measurement, smaller than the
curve-driven nodes.
| # | Task | Notes |
|---|---|---|
| 2.1 | Move standalone demo from `specificClass.js` to `examples/standalone-demo.js` | Pure deletion + move; tests unchanged. |
| 2.2 | Extract `basin/` (BasinGeometry + thresholdValidator) | Pure functions. |
| 2.3 | Extract `measurement/flowAggregator.js` (incl. `_updatePredictedVolume`) | Centerpiece of the tick loop. |
| 2.4 | Extract `measurement/measurementRouter.js` + `measurement/calibration.js` | |
| 2.5 | Extract `control/` strategies + dispatcher | levelBased, flowBased (stub), manual. |
| 2.6 | Extract `safety/safetyController.js` | dryRunRule + overfillRule split internally. |
| 2.7 | Add `getStatusBadge()` on `PumpingStation`; remove badge logic from nodeClass | |
| 2.8 | Convert `nodeClass.js` to extend `BaseNodeAdapter` | |
| 2.9 | Convert `specificClass.js` to extend `BaseDomain` | Use `ChildRouter`, `UnitPolicy`. |
| 2.10 | Extract `commands/` registry + handlers | Old topic names become aliases. |
| 2.11 | Extract `editor.js` from `pumpingStation.html` (the SVG redraw logic) | Served via a `/pumpingStation/editor.js` admin endpoint. |
| 2.12 | Generate `CONTRACT.md` from `commands/` + handwritten events section | |
| 2.13 | Tests: 3-tier per extracted module + the existing suite still green | Add edge tests for any regression discovered. |
| 2.14 | Docker E2E (deploy `01-basic`/`02-integration`/`03-dashboard` flows on a running Node-RED) | Required for "trial-ready" claim. |
## Phase 3 — measurement
| # | Task | Notes |
|---|---|---|
| 3.1 | Promote stats helpers to `generalFunctions/src/stats/` (already done in 1.10) | |
| 3.2 | Convert analog mode to use `Channel` internally (with `key=null`) | Removes the ~400-line inline pipeline duplication. |
| 3.3 | Extract `simulation/simulator.js` | |
| 3.4 | Extract `calibration/calibrator.js` | |
| 3.5 | Add `getStatusBadge()` on `Measurement` | |
| 3.6 | Convert `nodeClass.js` to `BaseNodeAdapter`; `specificClass.js` to `BaseDomain` | |
| 3.7 | Extract `commands/` | |
| 3.8 | `CONTRACT.md` | |
| 3.9 | Tests + Docker E2E | |
## Phase 4 — machineGroupControl
| # | Task | Notes |
|---|---|---|
| 4.1 | Extract `groupOps/` (groupOperatingPoint + groupCurves) | The cluster of `_group*` helpers. |
| 4.2 | Extract `totals/totalsCalculator.js` | |
| 4.3 | Extract `combinatorics/pumpCombinations.js` | |
| 4.4 | Extract `optimizer/bestCombination.js` + `optimizer/bepGravitation.js` | |
| 4.5 | Extract `efficiency/groupEfficiency.js` | |
| 4.6 | Extract `dispatch/demandDispatcher.js` using `LatestWinsGate` | Replaces `_dispatchInFlight`/`_delayedCall` directly. |
| 4.7 | Add `getStatusBadge()` | |
| 4.8 | Convert nodeClass + specificClass to base classes; use `ChildRouter` | |
| 4.9 | `commands/` + `CONTRACT.md` | |
| 4.10 | Tests + Docker E2E | |
## Phase 5 — rotatingMachine
| # | Task | Notes |
|---|---|---|
| 5.1 | Extract `curves/` (loader + normalizer + reverseCurve) | |
| 5.2 | Extract `prediction/` (predictors + groupPredictors + operatingPoint) | |
| 5.3 | Extract `drift/` using `HealthStatus` | |
| 5.4 | Extract `pressure/` (virtual children + initialization + router) | |
| 5.5 | Extract `state/stateBindings.js` (adapter to existing `generalFunctions/state`) | |
| 5.6 | Extract `measurement/measurementHandlers.js` | |
| 5.7 | Extract `flow/flowController.js` | |
| 5.8 | Extract `display/workingCurves.js` | |
| 5.9 | Add `getStatusBadge()` (replaces the 100-line nodeClass version) | |
| 5.10 | Convert nodeClass + specificClass | |
| 5.11 | `commands/` + `CONTRACT.md` | |
| 5.12 | Tests + Docker E2E | |
## Phase 6 — remaining nodes
For each: skeleton refactor only — extend `BaseNodeAdapter` + `BaseDomain`, use `ChildRouter`, move the input switch to `commands/`, add
`getStatusBadge()`. Domain-specific module split only if `specificClass` > 300 lines after the platform refactor.
| # | Task |
|---|---|
| 6.1 | `valve` |
| 6.2 | `valveGroupControl` |
| 6.3 | `diffuser` |
| 6.4 | `monster` |
| 6.5 | `settler` |
| 6.6 | `reactor` |
| 6.7 | `dashboardAPI` (special — likely no `BaseDomain`, it's a passive HTTP server) |
These are parallelisable — each can be its own agent.
## Phase 7 — remove legacy topic aliases
> **Note:** canonical names (`set.*`, `cmd.*`, `data.*`, `child.*`,
> `query.*`, `evt.*`) are used **from Phase 1 onwards** — see
> `CONTRACTS.md §1`. Each `commands/index.js` declares the canonical
> name as `topic` and lists pre-refactor names in `aliases`. So Phase 7
> is just the deprecation-window sweep.
| # | Task | Notes |
|---|---|---|
| 7.1 | Audit aliases across all `commands/` files; confirm one release cycle has elapsed | If any alias was added recently, defer that node's removal another cycle. |
| 7.2 | Remove `aliases` entries; canonical name only | Each removal is a single PR. |
| 7.3 | Update example flows that still used legacy names | Should already have been updated in their phase. |
| 7.4 | Document the removal in each `CONTRACT.md` | "Removed legacy topic X (replaced by canonical Y) on YYYY-MM-DD". |
## Phase 8 — promotion to main
When every node is on the new infra and Docker E2E green:
1. Bump submodule pointers in parent EVOLV `development`.
2. Open a PR per submodule (`development` → `main`).
3. Open the parent EVOLV PR last (`development` → `main`).
4. Merge in dependency order (`generalFunctions` first, then nodes that
depend on it, finally `EVOLV`).
## Phase 8.5 — `generalFunctions` deprecated path cleanup
Removes the deprecated paths flagged in `OPEN_QUESTIONS.md`. Runs after
promotion to `main` (so callers have stopped depending on the old
paths via the platform's own consumers).
### Targets to remove
| Path | Replaced by | First flagged |
|---|---|---|
| `src/helper/menuUtils_DEPRECATED.js` | `src/menu/` (the active menu manager) | pre-refactor |
| `loadCurve` export (in `index.js` + `datasets/assetData/curves/`) | `loadModel` | pre-refactor |
| Any `*_DEPRECATED.*` file added during the refactor | (per-file note) | refactor |
### Tasks
| # | Task | Notes |
|---|---|---|
| 8.5.1 | Audit consumers of `loadCurve` across all nodes | Should be zero after Phase 5 (rotatingMachine) — verify. |
| 8.5.2 | Remove `loadCurve` export + the underlying file | Single PR. Test all nodes. |
| 8.5.3 | Remove `menuUtils_DEPRECATED.js` | Verify zero imports first. |
| 8.5.4 | Sweep `generalFunctions/src/` for `_DEPRECATED.*` files; remove with consumer audit | One PR per file. |
| 8.5.5 | Update `generalFunctions` README to drop deprecated references | |
## Phase 9 — wiki cleanup (post-refactor)
Goal: each node's gitea wiki becomes **visual-first**, scannable, and
follows one shared template. Today's wiki has lots of prose and varies
per node — once the platform is uniform, the wiki should be too.
Don't start phase 9 until phase 8 is done (the wiki documents the
post-refactor shape, not the in-flight transition).
### Standard wiki template (one file per node, this is the spec)
```
1. One-paragraph "what is this node" (≤ 60 words).
2. Position in the platform — a Mermaid block showing the node and its
typical neighbours (parent + child types, with arrows for
data direction).
3. Capability matrix — small table of "what this node can do" with
✅ / ❌ / partial.
4. Topic contract — auto-generated from src/commands/index.js
(set.* / cmd.* / evt.* / data.* — payload schema and example).
5. Output payload — a Mermaid sequence-diagram of a typical tick
(parent → child → measurement → tick → port-0 emit).
6. Configuration — a Mermaid block diagram of the editor form sections
plus a table mapping each form field to the config key it lands at.
7. Examples — links to examples/01-basic, 02-integration, 03-dashboard
with one screenshot each.
8. State / mode chart — Mermaid stateDiagram for any node with
non-trivial states (rotatingMachine, pumpingStation, MGC).
9. "When you would NOT use this node" — explicit non-goals.
10. Issues / known limitations — single-line items with links to
repo issues.
```
### Tasks
| # | Task | Notes |
|---|---|---|
| 9.1 | Author the canonical wiki template at `.claude/refactor/WIKI_TEMPLATE.md` (or the repo-mem rule path) | Source of truth. |
| 9.2 | Build the auto-generator: `commands/index.js` → "Topic contract" markdown section | Run via a small `npm run wiki:contract` script per node. |
| 9.3 | Pilot on `pumpingStation` wiki: replace existing pages with the new template | Visual-first, prune prose. |
| 9.4 | Apply to other 3 core nodes (`measurement`, `MGC`, `rotatingMachine`) | |
| 9.5 | Apply to remaining nodes (one per repo) | |
| 9.6 | Update parent EVOLV wiki: top-level platform overview with a Mermaid block of all 13 nodes and how they connect (S88 hierarchy + data direction) | |
| 9.7 | Add a wiki style guide (max prose per section, where Mermaid is required, screenshot conventions) | |
| 9.8 | Audit pass: every page renders, every Mermaid block compiles, every link resolves | |
### Visual primitives we'll lean on (Mermaid)
- `flowchart LR` — node connections (parent ↔ child, data direction).
- `sequenceDiagram` — tick-to-port-0 lifecycle.
- `stateDiagram-v2` — rotatingMachine / pumpingStation state machines.
- `erDiagram` — only if a node has a complex internal data model worth
visualising.
Skip: classDiagram (we don't expose classes to users); gantt (no
schedules in a node's docs).
### Hard rules
- Every page leads with the Mermaid platform-position block. No "intro
paragraph then later a diagram" — diagram first.
- Each section opens with the diagram or table; prose annotates the
visual, not the other way round.
- No more than 60 words of unbroken prose anywhere on a page.
- One canonical source of truth for the topic contract: `commands/index.js`.
The wiki page is generated from it. No hand-written drift.
## Phase 10 — test-suite refactor (post-wiki)
Goal: bring every node's test layout in line with `CONVENTIONS.md §Testing`
now that the platform is uniform. Pre-existing test debt logged in
`OPEN_QUESTIONS.md` gets cleaned up here.
### Tasks
| # | Task | Notes |
|---|---|---|
| 10.1 | Audit each node: basic / integration / edge split, naming, helpers | One pass; produce a per-node punch list. |
| 10.2 | Convert any Mocha-style tests (`describe`/`it`) to `node:test` | Specifically `dashboardAPI/test/basic/structure-module-load.basic.test.js`. |
| 10.3 | Address `reactor` mathjs load (per OPEN_QUESTIONS): tree-shake or hoist | If hoisted, document the pattern as a CONVENTION addition. |
| 10.4 | Promote shared test helpers to `generalFunctions/test/helpers/` | Common fakes: fake Node-RED node, fake child, fake RED. |
| 10.5 | Add missing edge tests for each refactored module flagged during P2-P5 | Edge cases discovered during refactor land here. |
| 10.6 | Make every basic-test runner exit cleanly in batch (`node --test test/basic/`) | No leaked timers, no real `setInterval` outliving the assertions. Mirrors the BaseNodeAdapter test fix. |
| 10.7 | Standard CI shape: each node has `npm run test:basic`, `test:integration`, `test:edge` (consistent across nodes) | Allows uniform CI invocation. |
| 10.8 | Audit pass: every node's test suite green in batch under one wall-clock budget (≤ 60 s for basic) | The new platform-wide gate. |
## Phase 11 — unit-aware commands
Goal: every numeric setter / data topic carries an explicit unit; the user
can supply any compatible unit and the commandRegistry normalises before
the handler runs. Unknown units warn + list accepted alternatives.
### Tasks
| # | Task | Notes |
|---|---|---|
| 11.1 | `generalFunctions/src/convert/`: add `possibilities(measure)` helper | Returns the list of accepted unit names for a measure (`volumeFlowRate`, `pressure`, etc.). |
| 11.2 | `generalFunctions/src/nodered/commandRegistry.js`: handle `descriptor.units` | Normalisation pipeline: extract value+unit from msg, validate against `units.measure`, convert to `units.default`, warn + fall back on bad input. Tests for all 4 paths (no-unit / valid / wrong-measure / unknown). |
| 11.3 | `generalFunctions/src/nodered/BaseNodeAdapter.js`: auto-wire `query.units` topic | Returns `{ topic → { measure, default, accepted: [...] } }` from the registry. No per-node wiring needed. |
| 11.4 | `generalFunctions/scripts/wikiGen.js`: render `units` column | Topic-contract auto-gen table grows a Unit column showing `measure (default <unit>)`. |
| 11.5 | Per-node `src/commands/index.js`: declare `units` on every numeric setter | ~10 nodes. See proposed default-units table in interview reply. |
| 11.6 | Regenerate every `CONTRACT.md` + wiki `Home.md` via `npm run wiki:all` | Automated. |
| 11.7 | Tests: commandRegistry unit-handling paths | 4 scenarios per the validation table. |
### Default units per topic (proposed)
| Topic | Default | Why |
|---|---|---|
| pumpingStation `set.inflow` | `m3/h` | Operator-friendly scale |
| pumpingStation `set.demand` | `m3/h` | same |
| pumpingStation `set.outflow` | `m3/h` | symmetric |
| pumpingStation `cmd.calibrate.volume` | `m3` | basin volume |
| pumpingStation `cmd.calibrate.level` | `m` | basin height |
| MGC `set.demand` | `m3/h` | matches PS |
| rotatingMachine `set.setpoint` | `%` | control% |
| rotatingMachine `set.flow-setpoint` | `m3/h` | flow target |
| rotatingMachine `data.simulate-measurement` | per `payload.type` | dispatch by sensor type |
| valve `set.position` | `%` | valve open-% |
| measurement `data.measurement` | mode-dependent | analog → Channel scaling; digital → per-channel cfg |
| monster `data.flow` | `m3/h` | already enforced |
| reactor `data.influent` | flow=m3/h, concentrations=mg/L | engine internals |
| settler `data.influent` | flow=m3/h, concentrations=mg/L | matches reactor |
| diffuser `data.flow` | `m3/h` | air flow scale |