Files
EVOLV/.claude/refactor/Archive/TASKS.md

300 lines
16 KiB
Markdown
Raw Permalink Normal View History

# 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` | 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.
P8 prep: bump submodule pointers to development tips after Phase 1-6 All 12 submodules + parent EVOLV are now on the `development` branch with the platform refactor complete: generalFunctions 7372d12 Phase 1 platform infra (additive) BaseNodeAdapter / BaseDomain / UnitPolicy ChildRouter / LatestWinsGate / HealthStatus commandRegistry / statusBadge / statusUpdater stats — 113 unit tests pumpingStation 52d3889 Phase 2 — concern split + integration basin/measurement/control/safety/io/commands specificClass 1039→245 lines, 102 tests measurement 42a0333 Phase 3 — Channel-based analog + BaseDomain simulator/calibration/commands extracted specificClass 716→244 lines, 96 tests machineGroupControl bb2f3be Phase 4 — concern split + integration groupOps/totals/combinatorics/optimizer/ efficiency/dispatch/commands specificClass 1808→336 lines, 77 tests rotatingMachine e058fe9 Phase 5 — concern split + integration curves/prediction/drift/pressure/state/ measurement/flow/display/commands specificClass 1760→400 lines, 196 tests valve e27135b Phase 6 platform refactor + concern split valveGroupControl e02cd1a Phase 6 platform refactor + concern split diffuser 0ec9dd1 Phase 6 platform refactor (port 4→3) monster 2a6a0bc Phase 6 platform refactor + concern split settler b8247fc Phase 6 platform refactor (reactor link kept) reactor 7bf464b Phase 6 platform refactor + kinetics/ split dashboardAPI 2874608 Phase 6 — commandRegistry only (no BaseDomain; passive HTTP server — see OPEN_QUESTIONS.md) 493 basic tests pass platform-wide (12/12 nodes green). All canonical input topics (set.* / cmd.* / data.* / child.* / query.* / evt.*) live alongside legacy aliases with one-time deprecation warnings. Topic-rename cycle (P7) elapses across one release before alias removal. Decisions taken during the refactor are recorded in .claude/refactor/OPEN_QUESTIONS.md (resolved entries + carryovers for Phase 8.5 cleanup, Phase 9 wiki, and Phase 10 test rewrite). Ready for review on a per-submodule basis. Promotion to main is gated on Docker E2E (per-node trial-ready criteria) — not part of this commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:25:16 +02:00
## 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 |