- configs/machineGroupControl.json: drop prioritypercentagecontrol mode
(unused — set.demand became unit-self-describing, so percentage-vs-absolute
is decided per-message, not by a node-wide scaling mode). Add output.process
/ output.dbase enums + functionality.distance{,Unit,Description} so the
editor's distance offset persists. Fixes the runtime warnings 'Unknown key
optimization/scaling/movement/curvePressureUnit etc.' the validator was
logging on every MGC instantiation.
- configs/measurement.json: same output.process/dbase block + nullable
position.x for the rare case a measurement has no parent yet.
- datasets/assetData/machine.json -> rotatingmachine.json: rename so
AssetMenu's softwareType lookup matches. AssetMenu.getActiveCategoryKey
no longer silently falls back to keys[0] (which mis-showed diffuser models
for rotatingMachine nodes) — returns null with a console.warn instead.
- menu/asset.js: re-derive supplier/assetType from saved model id on reopen.
The save handler intentionally discards the denormalized registry copies
to keep the persisted node small, so the cascade dropdown booted at
'Select...' even when a model was saved. Walk the registry tree to
reconstitute.
- predict/predict_class.js: minor.
- configs/index.js: minor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B2.3 LatestWinsGate fireAndWait:
Added fireAndWait(value, ctx?) returning per-fire settlement promise.
Supersede resolves with frozen sentinel {superseded: true} (no
rejection — callers branch on value without try/catch). Dispatch
errors also resolve (with undefined); error surfaces via gate.lastError.
LatestWinsGate.js 75 → 116 lines. 12/12 tests pass.
P11.1 convert.possibilities(measure):
New helper returning sorted+deduped unit names for a measure.
Cached per measure. Reuses existing convert measures map. Also
exposed convert.measures() listing all known measures.
convert/index.js +21 lines. New test file: 90 lines, 12/12 tests.
P11.2 commandRegistry.units field:
Pre-dispatch normalisation pipeline. descriptor.units = {measure,
default}; commandRegistry extracts msg.payload + msg.unit (3 shapes),
validates against measure, converts to default, falls back + warns
with accepted-list on unknown/wrong-measure. Falls back gracefully
if convert.possibilities is missing. commandRegistry.js 164 → 237.
+7 new tests covering all 4 paths.
monster schema fix (P11.2 sibling):
generalFunctions/src/configs/monster.json was stripping four
legitimate constraint keys (nominalFlowMin, flowMax, maxRainRef,
minSampleIntervalSec). Added them with defaults matching the
legacy nodeClass coercion. Side effect: this also UNBLOCKED the
monster cooldown-guard test (separate ROOT-CAUSE entry below).
CONTRACTS.md §4 + §8 updated. 144/144 basic tests + 206/206 full
generalFunctions tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MeasurementContainer.isUnitCompatible now short-circuits to accept any unit
when the measurement type is not in the built-in measureMap. Known types
(pressure, flow, power, temperature, volume, length, mass, energy) still
validate strictly. This unblocks user-defined types in the measurement
node's new digital/MQTT mode — e.g. 'humidity' with unit '%', 'co2' with
'ppm' — without forcing those units into the convert-module unit system.
measurement.json schema: add 'mode.current' (analog | digital) and
'channels' (array) so the validator stops stripping them from the runtime
config. Ignored in analog mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>