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:
znetsixe
2026-05-10 19:44:42 +02:00
parent 91e4255ef5
commit 13da7388ff
2 changed files with 72 additions and 5 deletions

View File

@@ -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)}`,
]);
} }
} }

View File

@@ -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.
---