# Reference — Limitations ![code-ref](https://img.shields.io/badge/code--ref-b20a573-blue) > [!NOTE] > Pending full node review (2026-05). Content reflects `CONTRACT.md` and current source only. > > What `valveGroupControl` does not do, current rough edges, and open questions. Open items live in `.agents/improvements/IMPROVEMENTS_BACKLOG.md` in the superproject. --- ## When you would not use this node | Scenario | Use instead | |:---|:---| | A single valve under one upstream parent | `valve` — VGC adds coordination overhead for no benefit with one child. | | Valves in **series** rather than parallel | The Kv-share solver assumes parallel branches sharing a common header pressure. Series valves need their own coordination model. | | Upstream already publishes per-branch flow setpoints | Route the per-branch setpoints directly to each `valve` — VGC's group-level redistribution would discard the upstream split. | | Curve-based pumps grouped on a manifold | `machineGroupControl` — that's the pump-side equivalent (BEP-aware optimizer + planner). VGC has no curves and no optimizer. | --- ## Legacy file-naming drift The entry file and editor HTML still use the abbreviated `vgc.{js,html}` filenames: | Path | Currently | Should be | |:---|:---|:---| | Entry file | `vgc.js` | `valveGroupControl.js` | | Editor HTML | `vgc.html` | `valveGroupControl.html` | Per the EVOLV folder-naming convention (`.claude/rules/node-architecture.md` and `CLAUDE.md`), every per-node file MUST match the folder name exactly — no abbreviations. The rename is queued for the next touch: ``` files to update in one commit: - rename vgc.js → valveGroupControl.js - rename vgc.html → valveGroupControl.html - update package.json#node-red.nodes (currently maps "valveGroupControl": "vgc.js") - update any require() / import paths - update superproject submodule references ``` Sibling drift: `machineGroupControl/mgc.{js,html}` and `dashboardAPI/dashboardapi.{js,html}` are in the same state. See `.claude/refactor/MODULE_SPLIT.md`. --- ## Known limitations ### `set.position` is a no-op `commands/handlers.setPosition` intentionally does nothing — the handler debug-logs the payload and returns. Per-valve positional override is reserved pending Phase 7 topic standardisation of valve setpoint payloads. The canonical topic and alias are reserved so callers can't squat them in the meantime. Workaround: route the position setpoint to the individual `valve` node directly. ### `isValidActionForMode` is not implemented The schema declares `mode.allowedActions.` (`statusCheck` / `execSequence` / `emergencyStop` / `valvePositionChange` / `totalFlowChange` / `valveDeltaPchange`), but `specificClass.handleInput` only consults `isValidSourceForMode`. The action allow-list is effectively dead config. Source contradiction with `CONTRACT.md` which implies action gating is active. Workaround: rely on the source allow-list for now. TODO: implement the action check (mirror `rotatingMachine`'s pattern) OR strip `allowedActions` from the schema. ### `calculationMode` is not consulted Schema field `calculationMode` (`low` / `medium` / `high`) is declared but ignored by both `specificClass` and `nodeClass`. The tick interval is fixed at `tickInterval = 1000 ms` and only re-tunable via `set.reconcileInterval`. TODO: wire it through or remove. ### `mode.allowedSources.maintenance` is undefined The `maintenance` mode is enumerated in `mode.current` and `mode.allowedActions`, but `mode.allowedSources` only declares `auto` / `virtualControl` / `fysicalControl`. `isValidSourceForMode('any', 'maintenance')` returns `false` for every source — effectively monitoring-only. This may be intentional, but it's not stated explicitly in any contract. ### Residual solver assumes Kv share is a valid first estimate Pathological valve curves (very non-linear Kv vs position) may need more passes than the default `maxPasses: 2` to reach `residualTolerance: 0.001`. The loop exits gracefully but `lastFlowSolve.residual` carries the gap; `flow.predicted.atEquipment` reads only the sum of what was accepted. There is no editor field for `flowReconciliation` — it's a runtime-only object. TODO: expose `maxPasses` / `residualTolerance` in the editor. ### Cascaded VGC not test-covered `valvegroupcontrol` is in `SOURCE_SOFTWARE_TYPES` and `_registerSource` accepts it, so VGC-on-VGC cascades are wired by the router. But: - No integration tests cover the case. - No production deployments use it. - Fluid-contract propagation across two VGCs hasn't been validated. Treat as experimental. Open question whether to remove the entry or harden it. ### Multi-source aggregation is "last write wins" on shared positions If two upstream sources both publish `flow.predicted.atEquipment` to the same VGC, the later write replaces the earlier one in the measurement container. There is no merge / max / priority logic. In practice this is fine when one VGC has one upstream source; with two upstream sources the behaviour is well-defined but may surprise. ### Source flow events listen on both case variants `SOURCE_FLOW_EVENTS` includes both `flow.predicted.atEquipment` AND `flow.predicted.atequipment`. A source that emits the event twice (defensive code) will trigger `updateFlow` twice per change — harmless because the second call writes the same value, but it doubles the recompute. Open question whether to canonicalise the event name at the source. ### No FSM — sequences depend on `state` being pre-stamped operational `specificClass.configure()` does `this.state.stateManager.currentState = 'operational'` immediately so `executeSequence('startup')` etc. can run. This is unusual — other nodes go through `boot` to reach `operational`. The shortcut is intentional (VGC doesn't model the group as a stateful machine; sequences are pass-through to valves) but it means `state.getCurrentState()` always reads `operational` regardless of what the valves are doing. ### No `test/_output-manifest.md` Per `.claude/rules/output-coverage.md` every node should ship an output manifest with populated + degraded tests for each Port-0 / 1 / 2 key. **Not yet produced** for VGC. Backfill tracked in `.agents/improvements/IMPROVEMENTS_BACKLOG.md`. ### Wiki source-of-truth contradictions found during this review | Source A | Source B | Issue | TODO | |:---|:---|:---|:---| | `CONTRACT.md` `set.mode` payload "`auto` / manual" | Schema enum `auto` / `virtualControl` / `fysicalControl` / `maintenance`; `setMode` validates against schema | `CONTRACT.md` prose understates mode count | Update `CONTRACT.md`. | | Schema `mode.allowedActions` | `specificClass` only consults `isValidSourceForMode` | Action allow-list dead config | Implement or remove (see above). | | Schema `calculationMode` | `specificClass` / `nodeClass` never read it | Dead config | Implement or remove. | | `CONTRACT.md` § Children: `valve`, `machine / rotatingmachine / machinegroup / machinegroupcontrol / pumpingstation / valvegroupcontrol` | `specificClass.SOURCE_SOFTWARE_TYPES` lists post-canonicalisation names only (`machine`, `machinegroup`, `pumpingstation`, `valvegroupcontrol`) | Pre-canonical aliases (`rotatingmachine`, `machinegroupcontrol`) are accepted by the router because BaseDomain normalises them — contract text remains correct in spirit | None — informational. | | `examples/README.md` lists 3 flows with stub descriptions | Actual flow content not validated against current source | Tier labelling missing; live-deploy validation outstanding | Backfill validation; rename to Tier-1/2/3 convention. | --- ## Open questions (tracked) | Question | Where it lives | |:---|:---| | Phase 7 standardisation of valve setpoint payloads (unblocks `set.position`) | `OPEN_QUESTIONS.md` Phase 7 | | Should `flowReconciliation.maxPasses` / `residualTolerance` be editor-configurable? | Internal — not yet ticketed | | Cascaded `valvegroupcontrol` as upstream source — harden or remove? | Internal | | Multi-source priority / merge strategy for shared positions | Internal | | Wire `calculationMode` through or strip from schema | Internal | | Implement `isValidActionForMode` or strip `allowedActions` from schema | Internal | | Output-coverage backfill (`test/_output-manifest.md` + populated/degraded tests) | `.agents/improvements/IMPROVEMENTS_BACKLOG.md` | | Rename `vgc.{js,html}` → `valveGroupControl.{js,html}` | `.claude/refactor/MODULE_SPLIT.md` | | Validate example flows against live Node-RED; rename to Tier-1/2/3 convention | Internal | --- ## Migration notes ### From `setpoint` topic name (pre-canonical) The old `setpoint` alias for `set.position` still works but logs a one-time deprecation warning. Switch to `set.position` — though note the handler is currently a no-op (see above). ### From `setMode` / `registerChild` / `execSequence` / `totalFlowChange` / `emergencyStop` / `emergencystop` / `setReconcileInterval` aliases Every legacy alias emits a one-time deprecation warning. Switch to the canonical topic names listed in [Contracts](Reference-Contracts#topic-contract). --- ## Related pages | Page | Why | |:---|:---| | [Home](Home) | Intuitive overview | | [Reference — Contracts](Reference-Contracts) | Topic + config + child filters (alias map) | | [Reference — Architecture](Reference-Architecture) | Code map, flow-distribution loop, source aggregation | | [Reference — Examples](Reference-Examples) | Shipped flows + debug recipes | | [machineGroupControl — Limitations](https://gitea.wbd-rd.nl/RnD/machineGroupControl/wiki/Reference-Limitations) | Sibling Unit-level controller's known limitations | | [valve wiki](https://gitea.wbd-rd.nl/RnD/valve/wiki/Home) | The child node VGC coordinates |