refactor docs: lock in topic-prefix glossary, child-getters, opt-in tick
Resolves the 5 open questions answered during Phase 1 setup: - Topic naming: canonical from Phase 1 (set/cmd/data/child/query/evt), with full glossary in CONTRACTS.md §1. - Parent EVOLV branch lineage: rebased onto origin/main. - Deprecated paths: tracked as Phase 8.5 in TASKS.md. - Child storage: registry-as-truth + named getters via declareChildGetter. - Tick: opt-in via static tickInterval; default is event-driven via source.emitter 'output-changed'. statusInterval (always-on, 1Hz) is separate. Plus two new pre-existing-issue notes from the sanity gate: - dashboardAPI uses Mocha-style describe() under node:test (broken). - reactor tests are mathjs-bound (~13s/file load). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -233,12 +233,17 @@ class PumpingStation extends BaseDomain {
|
||||
}
|
||||
|
||||
// What the Node-RED status badge shows — see section 7.
|
||||
// Aggregators (no clean state machine) use compose. State-machine
|
||||
// nodes (rotatingMachine) use byState. Both return {fill, shape, text}.
|
||||
getStatusBadge() {
|
||||
return statusBadge.fromState({
|
||||
direction: this.flowAggregator.direction,
|
||||
vol: this.measurements.type('volume').variant('measured').position('atequipment').getCurrentValue('m3'),
|
||||
maxVol: this.basin.maxVolAtOverflow,
|
||||
});
|
||||
const direction = this.flowAggregator.direction;
|
||||
const vol = this.measurements.type('volume').variant('measured').position('atequipment').getCurrentValue('m3');
|
||||
const pct = (vol / this.basin.maxVolAtOverflow * 100).toFixed(1);
|
||||
const arrow = direction === 'filling' ? '⬆️' : direction === 'draining' ? '⬇️' : '⏸️';
|
||||
return statusBadge.compose([
|
||||
`${arrow} ${pct}%`,
|
||||
`V=${vol.toFixed(2)}/${this.basin.maxVolAtOverflow.toFixed(2)} m³`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,3 +97,65 @@ serialisation uses `LatestWinsGate` internally.
|
||||
See `CONTRACTS.md §2` for the BaseNodeAdapter shape.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-10 — ChildRouter wildcard subscriptions monkey-patch `emit`
|
||||
|
||||
**Context:** P1.2 implementation. EventEmitter has no native wildcard.
|
||||
Subscriptions with a partial filter (`{type}`-only or `{position}`-only)
|
||||
install a per-variant `emit` proxy on the child's emitter; concrete
|
||||
`{type, position}` filters use plain `emitter.on`.
|
||||
|
||||
**Question:** Multi-parent children. `child.parent` is already an array
|
||||
in `childRegistrationUtils`, so a child can be registered under several
|
||||
parents. If two parents each install ChildRouter wildcard proxies on
|
||||
the same `child.measurements.emitter`, the wraps stack — but
|
||||
`tearDown` only unwraps when its own bookkeeping is empty. Is this
|
||||
correct semantics for multi-parent teardown ordering? Or should we
|
||||
switch to per-listener fan-out (subscribe to every known
|
||||
`<type>.<variant>.<position>` enumerated from a registry)?
|
||||
|
||||
**Default chosen:** Stacked wrappers. The current `childRegistrationUtils`
|
||||
multi-parent path is rarely exercised in production. Revisit if
|
||||
Phase 2 / Phase 4 hits a real multi-parent case.
|
||||
|
||||
**Decision needed by:** Phase 4.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-10 — `predictionHealth` migration in rotatingMachine
|
||||
|
||||
**Context:** P1.4 implementation flagged that the existing
|
||||
`rotatingMachine.predictionHealth` carries `quality` (string) +
|
||||
`confidence` (0..1 numeric) on top of the new `HealthStatus` shape's
|
||||
`{level, flags, message, source}`.
|
||||
|
||||
**Question:** Where does `confidence` live after migration?
|
||||
|
||||
**Default chosen:** Keep `confidence` on the per-metric drift
|
||||
container as a sibling to a `health: HealthStatus` field. Drift
|
||||
diagnostics (`nrmse`, `longTermNRMSD`, `immediateLevel`) stay as
|
||||
siblings too. `HealthStatus` carries only the standardised five fields.
|
||||
|
||||
**Decision needed by:** Phase 5 (`rotatingMachine` refactor).
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-10 — `dashboardAPI` basic test broken (pre-existing)
|
||||
|
||||
**Context:** P1.12 sanity gate. `dashboardAPI/test/basic/structure-module-load.basic.test.js` uses Mocha-style `describe()` globals which don't exist under `node:test`. Reports 0 pass / 1 fail with `ReferenceError: describe is not defined`.
|
||||
|
||||
**Action:** Pre-existing — not caused by Phase 1. Convert to `node:test` form during Phase 6 when `dashboardAPI` gets its skeleton refactor. Tracked here so it isn't lost.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-10 — `reactor` test runtime is mathjs-bound (pre-existing)
|
||||
|
||||
**Context:** P1.12 sanity gate. Every reactor test file takes ~13 s because `require('mathjs')` alone is ~12.5 s on this machine (mathjs is huge and loads its full operator set eagerly). With basic tests parallelised by `node --test`, each subprocess pays the cost. A 90 s outer timeout doesn't accommodate the parallel load.
|
||||
|
||||
**Action:** Pre-existing — not caused by Phase 1. Two options to track for Phase 5/6 cleanup:
|
||||
1. Switch to a tree-shaken mathjs subset (only ops actually used).
|
||||
2. Cache the mathjs instance at module top and pass into Reactor classes.
|
||||
|
||||
Tracked; not blocking the refactor.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user