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.
|
// 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() {
|
getStatusBadge() {
|
||||||
return statusBadge.fromState({
|
const direction = this.flowAggregator.direction;
|
||||||
direction: this.flowAggregator.direction,
|
const vol = this.measurements.type('volume').variant('measured').position('atequipment').getCurrentValue('m3');
|
||||||
vol: this.measurements.type('volume').variant('measured').position('atequipment').getCurrentValue('m3'),
|
const pct = (vol / this.basin.maxVolAtOverflow * 100).toFixed(1);
|
||||||
maxVol: this.basin.maxVolAtOverflow,
|
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.
|
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