From a516c2b2b69328493956cc8d28e94febd2c3beee Mon Sep 17 00:00:00 2001 From: Rene De Ren Date: Wed, 6 May 2026 17:17:58 +0200 Subject: [PATCH] MeasurementContainer.get: strict-resolve explicit .child(name) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A read chain `.child(name).getCurrentValue()` previously fell through silently to the implicit-default child or the first available sibling when the named child did not exist. Caller asked for X, got Y, no warning. Surfaced via pumpingStation spillPrev: a fresh basin's .child('overflow').getCurrentValue() returned the value of 'manual-qout' (the only existing child at that position). Split the resolution into two strictness levels: _currentChildId (per-chain .child(name)) → STRICT, missing = null. this.childId (persistent setChildId) → HINT, falls back to 'default' then first. The persistent path is what registered children (rotatingMachine etc.) rely on: they write under composed ids ('up-') but expect reads without explicit .child() to still resolve. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/measurements/MeasurementContainer.js | 32 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/measurements/MeasurementContainer.js b/src/measurements/MeasurementContainer.js index 9ceb8af..5788f3d 100644 --- a/src/measurements/MeasurementContainer.js +++ b/src/measurements/MeasurementContainer.js @@ -425,16 +425,34 @@ class MeasurementContainer { // Legacy single measurement if (posBucket?.getCurrentValue) return posBucket; - // Child-aware: pick requested child, otherwise fall back to default, otherwise first available + // Child-aware lookup. Two separate sources of "child-id" on the + // container, with DIFFERENT strictness: + // + // _currentChildId : transient, set by .child(name) inside a chain. + // Explicit per-call. STRICT — if the named child + // does not exist, return null. Silent fall-through + // to a sibling would mask a missing-stream read + // as a wrong-stream read (see pumpingStation + // spillPrev bug, 2026-05-06). + // + // this.childId : persistent, set by setChildId(id). HINT only — + // try it first, then fall back to 'default' then + // first available. Containers registered with a + // persistent id (rotatingMachine, etc.) write + // under composed child ids (e.g. 'up-') that + // don't equal the persistent id, and reads must + // still resolve to those writes. if (posBucket && typeof posBucket === 'object') { - const requestedKey = this._currentChildId || this.childId; const keys = Object.keys(posBucket); if (!keys.length) return null; - const measurement = - (requestedKey && posBucket[requestedKey]) || - posBucket.default || - posBucket[keys[0]]; - return measurement || null; + + if (this._currentChildId) { + return posBucket[this._currentChildId] || null; + } + return (this.childId && posBucket[this.childId]) || + posBucket.default || + posBucket[keys[0]] || + null; } return null;