Files
EVOLV/.claude/refactor/MODULE_SPLIT.md

209 lines
10 KiB
Markdown
Raw Permalink Normal View History

# Per-node module split
Where each concern lives **after** the refactor. All paths are relative
to `nodes/<nodeName>/src/`.
## Generic node template (any node post-refactor)
```
nodes/<name>/
<name>.js # Node-RED entry: registerType + admin endpoints (≤ 50 lines)
<name>.html # Form template + thin oneditprepare/oneditsave (≤ 250 lines)
CONTRACT.md # Generated from commands/ + hand-written events
examples/
01-basic.json
02-integration.json
03-dashboard.json # optional
src/
nodeClass.js # extends BaseNodeAdapter; ~25 lines
specificClass.js # extends BaseDomain; orchestrator only; ~150 lines
editor.js # client-side JS for HTML, served via admin endpoint (only if non-trivial UI)
commands/
index.js # the command registry array
handlers.js # the handler functions
<concern>/ # one folder per domain concern (see per-node sections below)
...
test/
basic/
integration/
edge/
```
release: palette redesign + CoreSync scaffolding + dashboardAPI MODULE_NOT_FOUND fix 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>
2026-05-21 15:09:33 +02:00
## pumpingStation (Process Cell — L5, group `#0c99d9` · palette `#8B4513`)
```
src/
nodeClass.js # ~25 lines, extends BaseNodeAdapter
specificClass.js # ~150 lines, orchestrator
editor.js # extracted SVG/redraw logic from the .html (~260 lines)
commands/
index.js # set.mode | set.demand | set.inflow | calibrate.* | child.register
handlers.js
basin/
BasinGeometry.js # initBasinProperties + level<->volume conversions
thresholdValidator.js # _validateThresholdOrdering — pure function
measurement/
flowAggregator.js # _selectBestNetFlow + _updatePredictedVolume + _computeRemainingTime + _levelRate + _deriveDirection
measurementRouter.js # _handleMeasurement + _onLevelMeasurement + _onPressureMeasurement
calibration.js # calibratePredictedVolume + calibratePredictedLevel + setManualInflow
control/
levelBased.js # _controlLevelBased + _scaleLevelToFlowPercent + _applyMachineGroupLevelControl
flowBased.js # placeholder for the flow mode; clearly stubbed
manual.js # forwardDemandToChildren
index.js # { 'levelbased': ..., 'flowbased': ..., 'manual': ... }
safety/
safetyController.js # evaluate() — split internally into dryRunRule + overfillRule
io/
statusBadge.js # getStatusBadge composition (was nodeClass._updateNodeStatus)
output.js # getOutput, mostly a pass-through to measurements + basin snapshot
configBuilder.js # extracted _loadConfig mapping
examples/
standalone-demo.js # extracted from the bottom of specificClass.js
```
release: palette redesign + CoreSync scaffolding + dashboardAPI MODULE_NOT_FOUND fix 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>
2026-05-21 15:09:33 +02:00
## measurement (Control Module — L2, group `#a9daee` · palette `#D4A02E`)
The good news: `Channel.js` already exists and is pure. Most of the
analog mode in `specificClass.js` is duplication that vanishes when the
analog path also goes through `Channel`.
```
src/
nodeClass.js # extends BaseNodeAdapter
specificClass.js # ~150 lines, orchestrator over modes
channel/
Channel.js # KEEP — already clean, the model for everything else
modes/
analogMode.js # one Channel built from flat config; routes msg.payload number
digitalMode.js # N channels from config.channels[]; routes msg.payload object
index.js # { analog, digital }
simulation/
simulator.js # simulateInput — random walk over the configured range
calibration/
calibrator.js # calibrate + isStable + standardDeviation helpers (drop duplicates of the static helpers in Channel)
commands/
index.js # set.simulator | set.outlierDetection | cmd.calibrate | data.measurement
handlers.js
```
`statistics/` (mean/stdDev/median/etc.) — promote to
`generalFunctions/src/stats/`. Both `Channel.static helpers` and the
calibrator use them.
release: palette redesign + CoreSync scaffolding + dashboardAPI MODULE_NOT_FOUND fix 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>
2026-05-21 15:09:33 +02:00
## machineGroupControl (Unit — L4, group `#50a8d9` · palette `#B5651D`)
```
src/
nodeClass.js # extends BaseNodeAdapter
specificClass.js # ~200 lines orchestrator; tick/handlePressureChange/handleInput
groupOps/
groupOperatingPoint.js # _equalizeOperatingPoint, _readChildMeasurement, _writeMeasurement
groupCurves.js # _groupFlow, _groupPower, _groupNCog, _groupCalcPower
totals/
totalsCalculator.js # calcDynamicTotals, calcAbsoluteTotals, activeTotals
combinatorics/
pumpCombinations.js # validPumpCombinations + checkSpecialCases
optimizer/
bestCombination.js # calcBestCombination (CoG-based)
bepGravitation.js # calcBestCombinationBEPGravitation + redistributeFlowBySlope + estimateSlopesAtBEP
index.js # picks the optimizer by config
efficiency/
groupEfficiency.js # calcGroupEfficiency + calcDistanceBEP + helpers
dispatch/
demandDispatcher.js # uses LatestWinsGate; handleInput + per-machine fanout
registration/ # auto via ChildRouter — file may be tiny
commands/
index.js # set.mode | set.scaling | set.demand | child.register
handlers.js
```
release: palette redesign + CoreSync scaffolding + dashboardAPI MODULE_NOT_FOUND fix 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>
2026-05-21 15:09:33 +02:00
## rotatingMachine (Equipment Module — L3, group `#86bbdd` · palette `#E89B3A`)
The biggest specificClass (1760 lines). The split mirrors the natural
boundaries the existing comments suggest.
```
src/
nodeClass.js # extends BaseNodeAdapter
specificClass.js # ~250 lines orchestrator
curves/
curveLoader.js # loadCurve wrapper + model resolution
curveNormalizer.js # _normalizeMachineCurve + _normalizeCurveSection (unit conversion + anomaly detection)
reverseCurve.js # the existing reverseCurve helper
prediction/
predictors.js # owns predictFlow / predictPower / predictCtrl (delegates to generalFunctions/predict)
groupPredictors.js # group-scope predictors used when an MGC parent calls setGroupOperatingPoint
operatingPoint.js # current operating point: pressure source, derived flow & power
drift/
driftAssessor.js # _updateMetricDrift + assessDrift + _applyDriftPenalty
predictionHealth.js # composes flow/power/pressure drift into a HealthStatus
pressure/
virtualChildren.js # _initVirtualPressureChildren + dashboard-sim children
pressureInitialization.js # getPressureInitializationStatus + tracking real children
pressureRouter.js # updateMeasuredPressure + per-position handling
state/ # adapter to generalFunctions/state — thin glue, lifecycle hooks
stateBindings.js # the position/state event handlers that fire _updateState etc.
measurement/
measurementHandlers.js # updateMeasured{Flow,Power,Temperature} + _callMeasurementHandler
flow/
flowController.js # handleInput dispatch by source/action/parameter — feeds state machine
display/
workingCurves.js # showWorkingCurves + showCoG (admin endpoints)
commands/
index.js # set.mode | cmd.startup | cmd.shutdown | cmd.estop | cmd.setpoint | cmd.flow-setpoint | data.simulate-measurement | query.curves | query.cog
handlers.js
```
## remaining nodes (skeleton — they get the platform refactor only)
| Node | Notes |
|---|---|
| `valve` | Equipment Module. Smaller than rotatingMachine — concern split likely just `state/`, `commands/`, `position/`. |
| `valveGroupControl` | Unit. Similar to MGC but no flow-power optimization — straightforward `position-aggregator` + `commands/`. |
| `reactor` | Unit. Domain is biological kinetics (ASM); will need a `kinetics/` folder. Big — second-tier candidate for deeper split. |
| `settler` | Unit. Has the recently-fixed `_connectReactor` integration; keep that wired through `ChildRouter`. |
| `monster` | Unit. Multi-parameter monitoring; the parameter set itself is config-driven. |
| `diffuser` | Equipment Module. Aeration controller. Likely small. |
| `dashboardAPI` | Utility. InfluxDB endpoints. Likely no `BaseDomain` — it's a passive HTTP server. |
release: palette redesign + CoreSync scaffolding + dashboardAPI MODULE_NOT_FOUND fix 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>
2026-05-21 15:09:33 +02:00
Palette swatches for these (sidebar): `valve` `#3CAEA3`, `valveGroupControl` `#2A8A82`, `reactor` `#6FAE5F`, `settler` `#8FAD3F`, `monster` `#9C5BB0`, `diffuser` `#6EB5E5`, `dashboardAPI` `#7A8BA3`. Group-box hex still follows S88 level (see `.claude/rules/node-red-flow-layout.md` §10.0).
The "skeleton" refactor for these is just:
- Convert `nodeClass.js` to extend `BaseNodeAdapter`.
- Convert `specificClass.js` to extend `BaseDomain`.
- Move the input switch to `commands/`.
- Add `getStatusBadge()` if not present.
- Use `ChildRouter` for registration.
- File splits driven by file size — if `specificClass` < 300 lines, leave it alone for now.
## generalFunctions itself
```
src/
configs/ # unchanged — JSON schemas per node
helper/ # eventually split into infra/ + domain/, but not in this refactor
measurements/ # MeasurementContainer — unchanged
nodered/ # NEW — node-RED-side infra
BaseNodeAdapter.js
commandRegistry.js
statusBadge.js # composition helpers
statusUpdater.js # the 1 Hz status-loop wrapper
index.js
domain/ # NEW — domain-side infra
BaseDomain.js
UnitPolicy.js
ChildRouter.js
LatestWinsGate.js
HealthStatus.js
index.js
stats/ # NEW — promoted from measurement (mean, std, median, mad, lerp)
index.js
```
Existing exports (`logger`, `configManager`, `outputUtils`,
`MeasurementContainer`, `predict`, `interpolation`, `state`, …) stay
exactly where they are. Imports keep working unchanged.
`generalFunctions/index.js` adds new exports alongside existing ones.
Nothing is removed in this refactor.