Files
pumpingStation/test/_output-manifest.md
Rene De Ren 4889fdaaf0 docs(contract): close output-contract gaps — mode/manualDemand, Port-2 topic, output manifest
- wiki/Reference-Contracts.md: regenerate data-model (npm run wiki:all) so the
  two live getOutput() keys `mode` and `manualDemand` are documented; refresh
  stale sample values; bump code-ref badge -> a83a85e; add human note describing
  the two control-state keys.
- CONTRACT.md: fix Port-2 outgoing topic registerChild -> child.register
  (registerChild is the deprecated *input* alias, not what the node emits).
- test/_output-manifest.md: add the mandatory output manifest (Port 0/1/2 +
  emitter events + the 15-way fn_status_split fan-out) with honest coverage gaps.
- test/integration/basic-dashboard-flow.test.js: fix stale fan-out count 14->15
  (output 14 = percControl chart added upstream); assert out[14].

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:13:11 +02:00

6.6 KiB
Raw Blame History

pumpingStation output manifest

Single source of truth for what this node emits and where it is tested, per .claude/rules/output-coverage.md. Generated against code-ref a83a85e. Regenerate the wiki contract with npm run wiki:all and re-check this table whenever getOutput(), src/commands/index.js, or an examples/*.json fan-out changes.

Null convention for this node: a Port-0 key whose source is not yet available is emitted as explicit null (e.g. timeleft, flowSource, manualDemand outside manual mode), never silently absent. Delta-compression on Port 0 then drops keys whose value is unchanged since the previous tick.

Port 0 (process data) — specificClass.getOutput()outputUtils.formatMsg(..., 'process')

msg.topic = config.general.name. Keys below are the full pre-delta-compression set.

Key Source Type States tested Test file
mode getOutputthis.mode string (levelbased/manual/flowbased/none) populated (manual) test/basic/specificClass.test.js
manualDemand getOutput_manualDemand number m³/h, null outside manual populated, null test/basic/specificClass.test.js
direction getOutputstate.direction string (filling/draining/steady) present test/basic/specificClass.test.js
flowSource getOutputstate.flowSource string, null when no source null (pre-child) test/basic/specificClass.test.js
timeleft getOutputstate.seconds number s, null when steady present, null test/basic/specificClass.test.js
percControl getOutputcontrolState.percControl number % 0..100 0, 25, 50, 75, 85, 100 test/basic/specificClass.test.js
dryRunLevel _computeSafetyPoints number m populated test/basic/specificClass.test.js
dryRunSafetyVol _computeSafetyPoints number m³ populated test/basic/specificClass.test.js
highVolumeSafetyLevel _computeSafetyPoints number m populated test/basic/specificClass.test.js
highVolumeSafetyVol _computeSafetyPoints number m³ populated test/basic/specificClass.test.js
predictedOverflowVolume measurements overflowVolume number m³ populated, 0 test/basic/specificClass.test.js
predictedOverflowRate measurements flow.overflow number m³/s populated, 0 test/basic/specificClass.test.js
predictedUnderflowVolume measurements underflowVolume number m³ 0 test/basic/specificClass.test.js
volume.predicted.atequipment.<childId> measurements.getFlattenedOutput number m³ populated test/basic/specificClass.test.js
basin geometry: heightBasin, surfaceArea, maxVol, minVol, maxVolAtOverflow, minVolAtInflow, minVolAtOutflow, volEmptyBasin, inflowLevel, outflowLevel, overflowLevel, inletPipeDiameter, outletPipeDiameter, minHeightBasedOn basin.snapshot() number (m/m²/m³) / string populated test/basic/specificClass.test.js, test/basic/BasinGeometry.basic.test.js

Port 1 (InfluxDB telemetry) — formatMsg(..., 'influxdb')

Same key set as Port 0 (formatted via the influxdb formatter rather than process). Field names == Port-0 keys; config.general.name is the measurement tag. No Port-1-only fields. Covered transitively by the Port-0 tests above; a dedicated Port-1 line-protocol assertion is a gap (see below).

Port 2 (registration / control plumbing) — BaseNodeAdapter._scheduleRegistration

Topic Source Payload shape States tested Test file
child.register BaseNodeAdapter.js:122 { topic:'child.register', payload:<node.id>, positionVsParent, distance } (gap — see below)

Note: the canonical outgoing topic is child.register (matching the input registry). Earlier docs said registerChild; that is the deprecated input alias, not what this node emits.

Child-facing events — measurements.emitter

Fired as <type>.<variant>.<position> when a series receives a value. Parents subscribe by event name (data-driven, not a fixed catalogue):

Event When Test file
volume.predicted.atequipment each integrator tick test/basic/flowAggregator.basic.test.js
level.predicted.atequipment recomputed from volume test/basic/specificClass.test.js
flow.predicted.in (child manual-qin) set.inflow handler test/basic/measurementRouter.basic.test.js
overflowVolume/underflowVolume/flow.predicted.overflow integrator hits a physical bound test/basic/flowAggregator.basic.test.js

Example-flow function-node fan-out

examples/02-Dashboard.json :: fn_status_split (outputs: 15)

# Target widget Payload Populated Degraded/null
0 ui-text "Mode" string ✔ structure gap
1 ui-text "Direction" string gap
2 ui-text "Level" number m gap
3 ui-text "Volume" number m³ gap
4 ui-text "Volume %" number % gap
5 ui-text "percControl" number % gap
6 ui-text "Manual demand" number m³/h or — gap gap
7 ui-chart "Level (m)" {topic,payload:number} or no-msg gap
8 ui-chart "Volume (m³)" gap
9 ui-chart "Volume %" gap
10 ui-chart "Flow (m³/h)" — Inflow gap
11 ui-chart "Flow (m³/h)" — Outflow gap
12 ui-chart "Flow (m³/h)" — Net gap
13 ui-template "Raw output table" whole object (array) gap
14 ui-chart "percControl" {topic:'percControl',payload:number} gap

Populated/structure coverage: test/integration/basic-dashboard-flow.test.js (asserts output count = 15 and routes outputs 014). Degraded/empty-input coverage (no payload:null reaching any ui-chart) is still a gap — see below.

Known coverage gaps (tracked, prospective per the rule)

The output-coverage rule applies prospectively. Outstanding items for this node:

  • Dedicated test/basic/output-port0.test.js exercising every key above in both populated and degraded (pre-tick / null) states.
  • Port-1 line-protocol assertion (field names + tag).
  • Port-2 child.register payload-shape test.
  • fn_status_split degraded/empty-input fan-out test (no payload:null to any ui-chart) — the failure mode the rule was written for. The structure test in basic-dashboard-flow.test.js covers the populated path only.