Lines now have a minimum 3.7% gap so labels never collide with adjacent
threshold lines, regardless of how close the underlying levels are.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Canvas elements now use 'constraint: { horizontal: scale, vertical: scale }'
with percentage-based margin placement so the tank fills the card edge to
edge and stays centered as the panel resizes. Threshold labels split
left/right with a gap and at font size 14 for readability.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tank vertically centered in canvas frame. Labels for thresholds at the
very bottom of the tank (small dryRunThresholdPercent) no longer extend
below the visible card area.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Threshold labels now sit above or below their lines (never on top) and
are centered horizontally inside the tank.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Canvas frame stretched vertically to match the card's aspect ratio so
the tank visual fills the entire card height with no letterboxing below.
Redundant in-canvas readouts dropped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tank Layout visual now fills the Canvas card edge-to-edge. Each
threshold's name + value live INSIDE the tank near its line instead of
in external label columns.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Canvas card shrunk to w:6 and frame to 400 px so the basin visual fills
the card edge-to-edge. Level/Volume timeseries widen to w:14 to absorb
the freed columns. Right value labels and bottom readouts no longer clip.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tank Layout canvas + bar gauge + Level/Volume timeseries grow to h:20
so the basin visual occupies more vertical space and reads at a glance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces Heights/Volume-Limits/Fill% stats with an integrated basin visual:
vertical bar gauge bound to live level + threshold markers, plus Canvas
showing tank zones, threshold lines, named labels, and live readouts.
Level + Volume timeseries reflow next to the basin in the renamed Basin row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- machineGroupControl f18f3cc: fn_chart_pump_a/b/c emit -1 OFF sentinel on the
per-pump % control chart when state is off/idle/maintenance; ui_chart_pumps_ctrl
ymin=-5; new per-pump-ctrl-fanout output-coverage test + manifest update.
- pumpingStation e041877: revert canonical flow to m³/s (platform convention),
keep output m³/h for dashboard parity. No demand smoothing/hysteresis — that
belongs in a dedicated intermediate node per design review.
Also cleaned stale InfluxDB series (out-of-tree): dropped "MGC Isolated" and
"MGC — Pump Group" measurements and the category="undefined" rows in
"Machine Group" (135,416 stale rows; live untagged data preserved).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
machineGroupControl 2af6c90, generalFunctions 5c091cd. Rendezvous lock verified
live on the isolated rig: clean monotonic 1→2 pump staging, no wait/hunt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bumps nodes/dashboardAPI submodule to slice/34-walking-skeleton@7fdab73
(credentials block for bearer token, folderUid config field, basic tests).
- Pins grafana/grafana to 11.3.0 — legacy /api/dashboards/db is the
generator target; G12 K8s-style API is out of scope (PRD constraint).
Refs #34
Bumps machineGroupControl (e1e1977) and pumpingStation (ef07f2a) — example
dashboard JSON tweaks committed on each submodule's development branch.
Adds docs/research/ and docs/prd/ for the dashboardAPI v2 graph-aware Grafana
generator workflow (Gitea issues #32-#43). Ignores .prototypes/ — throwaway
spike code lives there per the /prototype skill.
Bumps:
- rotatingMachine 455f15d refactor: route unit conversions through UnitPolicy.convert
- pumpingStation 2d68a4f refactor + fix(level): UnitPolicy adoption, level-rate timestamp fix, integration test rewire
- machineGroupControl ddf2b07 refactor: _canonicalToOutputFlow + setDemand via UnitPolicy.convert, structure test rewire
- generalFunctions bc79de1 fix(influx): accept tagCode camelCase + emit positionVsParent tag
- measurement 36eaa2f test(edge): align with object-payload accept behaviour
The UnitPolicy bump finishes the §6 contract migration the refactor
plan named (drop _convertUnitValue / hardcoded m3/h<->m3/s scalars in
favour of policy.convert at every site).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the old 3-panel coresync-frost-demo.json with a 13-panel dashboard
designed for at-a-glance verification of CoreSync's compression behaviour.
Dashboard rebuild (docker/grafana/provisioning/dashboards/coresync-frost-demo.json):
- Header "How to read" text panel: definitions table + sanity checks so
every metric is line-of-sight to its Flux source.
- Scoreboard row (4 stats): raw samples / CoreSync knots / reduction % /
approx. bytes saved over the selected time range.
- Per-stream verification table: one row per CoreSync stream with raw,
knots, and reductionPct (gradient-coloured). Each line's math is
mentally checkable: raw × (1 − reductionPct/100) = knots.
- Signal-reconstruction overlays: flow (m³/h) and pressure (mbar)
rendered as a thin raw line plus fat red knot points so you can see
knots snap to the raw signal at direction changes. Fixes the previous
panels which mislabelled both as `flowm3h` regardless of units.
- Diagnostics row: per-stream knot-interarrival timeseries and a
full-math compression-health table (raw, knots, kept fraction with
gradient bar, savedPct with colour background).
Bumps coresync submodule to 21d77a8 which lands the FROST demo flow plus
the burst-window reducer fix that was driving cog/efficiency/SEC to ~0%
compression. Verified end-to-end on the live stack: headline reduction
went from 33% to 83%, broken streams from 0.6%-14% to 78%-93%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PALETTE REDESIGN (2026-05-21)
Sidebar swatches switched from S88 level (all blue) to domain-hue per node.
Family hue = function (rotating=orange, valves=teal, biology=green/olive,
sampling=violet, sensor=amber, aeration=sky-blue, infrastructure=slate);
within a family, darker = higher S88 / "more controller-ish."
Editor-group rectangles in flow.json still follow S88 — only the
registerType colour changed.
Submodule bumps for palette: rotatingMachine, machineGroupControl,
pumpingStation, valve, valveGroupControl, reactor, settler, monster,
measurement, diffuser, dashboardAPI.
Docs touched:
- CLAUDE.md: palette swatch vs. editor-group bullets split out.
- .claude/rules/node-red-flow-layout.md: new §10.0 introduces the two
color systems, full 12-row palette table, and explicit warning not to
mix the two hexes.
- .claude/refactor/MODULE_SPLIT.md: per-node headers annotated with
both `group #XXX` and `palette #XXX`.
- .claude/refactor/WIKI_HOME_TEMPLATE.md + WIKI_TEMPLATE.md: clarify
Mermaid classDefs visualize hierarchy, not palette swatches.
- .claude/refactor/OPEN_QUESTIONS.md: dated decision entry with
rationale, file list, and follow-ups.
CORESYNC SUBMODULE (new)
nodes/coresync added pointing at https://gitea.wbd-rd.nl/RnD/coresync.
FROST/SensorThings handoff path — first version forwards FROST-ready HTTP
request messages on the dbase output; a downstream http-request node
performs the POST and feeds responses back on msg.topic = "frost.response".
Lazy stream resolver, latest-wins queue (keep first + latest, drop middle),
knot-emit on slope change, provenance preserved in Observation parameters.
- .gitmodules: add nodes/coresync entry.
- package.json: register coresync as a Node-RED node.
- generalFunctions bump: new frostFormatter + 4 node config schemas
expose the dbase format option.
- measurement bump: "frost" option added to dbaseOutputFormat dropdown
(plus the in-flight data.measurement unit-handling work).
- machineGroupControl bump: small editor compact-fields tweak alongside
the palette change.
- CORESYNC_FROST_INTERVIEW_HANDOFF.md added at root with interview state
(Q20 open: slope angle vs. relative delta comparison).
DASHBOARDAPI MODULE_NOT_FOUND FIX
package.json: dashboardapi entry path corrected to
nodes/dashboardAPI/dashboardAPI.js. Commit e04c4a1 renamed the files to
camelCase but missed package.json; on case-sensitive filesystems
(Linux/Docker, where the tarball lands) the require resolved to nothing
and the node showed MODULE_NOT_FOUND in the Node-RED palette.
MISC CLEANUP
- examples/README.md + examples/pumpingstation-complete-example/ removal
(build_flow.py, flow.json, README.md superseded by per-node examples).
- jest.config.js: in-progress tweak.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Submodule pointer:
- rotatingMachine @ 8c5822c
style(editor): drop fixed max-width on rotor SVG — let it fill the panel
Repo hygiene:
- Add .repo-mem/, .codex, CLAUDE.local.md to .gitignore. These are
per-developer Claude Code state (memory store, IDE marker, per-machine
conventions) that shouldn't ever land in the repo. .repo-mem alone is
hundreds of MB on disk.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Submodule pointer updates:
- generalFunctions @ ae30cef
feat(pumpingStation schema): add holdLevel + deadZoneKeepAlivePercent;
slim npm pack
- machineGroupControl @ aeb938c
feat(setDemand): surface specificClass.setDemand(value, unit='%')
+ slim npm pack
- pumpingStation @ 2e4ad8d
fix(levelBased): drop hold zone, route through MGC.setDemand, add
holdLevel + integrator variant pick; slim npm pack
Cross-submodule summary:
- pumpingStation level-based control now sends percent demand to MGC via
the new MGC.setDemand entry point — was calling handleInput with a raw
percent, which the dispatcher interpreted as canonical m³/s and pegged
the group at 100 %.
- Ramp foot is no longer pinned at inflowLevel. Default is startLevel
(0 % at startLevel = MGC flow.min, matching operator mental model). New
optional holdLevel raises the 0 %-foot for an explicit hold band.
- Predicted-volume integrator now picks the best-available variant per
side (measured first, then predicted) so a real upstream sensor +
predicted pump outflow both feed the basin balance.
- Each submodule grew a .npmignore mirroring its .gitignore plus the
dev-only trees (test/, wiki/, .claude/, …). Per-submodule pack sizes
dropped — pumpingStation went 1.5 MB → 57 kB.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two trailing items the prior commits missed:
- .mcp.json was rm'd from the working tree in commit d4e72f2 (repo-mem
cleanup) but never staged for deletion; git status kept showing
' D .mcp.json' as a result. Properly removed now.
- nodes/pumpingStation pin bumps to pull in the wiki-gen regen commit
that kept Reference-Contracts.md in sync with the tool's canonical
output.
User WIP intentionally not touched: examples/README.md zeroed-out,
examples/pumpingstation-complete-example/* removed, and
nodes/rotatingMachine working-tree change to rotatingMachine.html.
Those belong to the user; the harness only re-flagged them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Superproject:
- CLAUDE.md: legacy-drift table loses the dashboardAPI row (migrated);
drift section notes the type-id-preservation strategy for the
remaining mgc / vgc renames.
- CONTRACTS.md: canonical-unit rule explicitly carves out reactor as
an approved ASM-textbook exception with the conversion boundary.
Submodules:
- nodes/valve @ 167b102: CONTRACT documents valve's lack of an FSM
maintenance state (schema mode enum accepts `maintenance` but no
enter/exit sequences exist). Limits made explicit instead of being
hidden as a wiki TODO.
- nodes/reactor @ 75d0413: CONTRACT now declares the approved ASM-unit
divergence (mg/L, m³/d, °C, 1/h) with the conversion boundary spelled
out. Closes the canonical-unit drift surfaced by the wiki audit.
- nodes/dashboardAPI @ ......: file rename (preserves type id).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- nodes/generalFunctions @ 8252a5f: schemas drop dead `calculationMode`
(RM/valve/VGC — never read) and dead `allowedActions` (valve/VGC —
flowController only checks isValidSourceForMode, never the action
allow-list). Kept allowedActions in RM/MGC where it IS enforced.
- nodes/{rotatingMachine,machineGroupControl,valve,valveGroupControl}:
set.mode descriptions in commands/index.js were generic "auto /
manual" but each node's schema declares 3–4 specific modes. Now
enumerate the actual allowed values and point at the schema.
- nodes/machineGroupControl CONTRACT.md additionally fixed: old
`prioritycontrol`, `optimalcontrol`, `dynamiccontrol` list was wrong
(lowercase + nonexistent dynamiccontrol); now matches the schema.
- nodes/monster: reframed from "multi-parameter biological process
monitor" to "sampling-cabinet pulse counter" — the code only emits
volumetric pulse + bucket state, no constituent analysis. Old
framing was misleading. examples/README also cleaned of references
to 4 flow files that don't exist on disk.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two cross-submodule fixes for the reactor unit-confusion drift the wiki
uplift surfaced:
- nodes/generalFunctions @ 4f715e8: reactor schema timeStep.unit now "s"
(was "h"), default 1 (was 0.001). Matches reactor.html label [s] and
baseEngine.js's seconds→days conversion. Description warns future
readers off changing the unit without updating the engine.
- nodes/reactor @ 346a3ce: HTML X_A_init default 0.001 → 200 (operational
nitrifying biomass; 0.001 disabled nitrification per the memory note).
Factory updated to match. New regression test
test/basic/timestep-units.basic.test.js locks in the seconds contract
and schema agreement. 49/49 reactor tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the placeholder topic tables in Reference-Contracts.md (left by
the wiki uplift agents) with authoritative content generated from each
node's src/commands/index.js. CI gate: `node tools/wiki-gen/bin/wiki-gen.js --check`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each node's wiki now matches the rotatingMachine reference: Home +
Reference-{Architecture,Contracts,Examples,Limitations} + _Sidebar.
Total ≈ 9 000 lines of wiki content. Topic-contract and data-model
sections wrapped in <!-- BEGIN AUTOGEN --> markers for the future
wiki-gen tool.
Each agent surfaced real source-vs-spec drift rather than inventing
content:
- measurement: digital-mode notifyOutputChanged path uncertain;
buildDomainConfig drops several editor fields; position case
preserved by child.register payload
- valve: flowController gates by source only (action allow-list dead);
no enter/exit maintenance sequences; sequence-abort token status TBD
- reactor: timeStep unit confusion (HTML s, schema h, engine /86400);
X_A default disagreement (0.001 vs 200 mg/L); doesn't honour the
canonical-unit rule (m³/d not m³/s, °C not K)
- valveGroupControl: 4 CONTRACT/source drifts incl. set.mode covers
4 modes not 2, calculationMode + mode.allowedActions dead config,
maintenance source allow-list undefined
- settler: icon colour #e4a363 conflicts S88 Unit #50a8d9; Port 2
emits null in _emitOutputs (init-only registration)
- diffuser: stale test/README claiming "no runtime" — runtime exists
(284-line specificClass with full OTR/ΔP model); update README
- monster: superproject docs frame as multi-parameter biological
monitor (NH4/NO3/COD/TSS), but source emits only volumetric pulse +
bucket state; 4 example flows referenced but missing on disk
- dashboardAPI: dashboardapi.{js,html} lowercase entry vs folder
convention; logging.enabled default mismatch (false vs true)
- generalFunctions: full library API restructure preserved; module
map + consumer graph + extension examples retained
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Delete .mcp.json + .claude/rules/repo-mem.md; drop .repo-mem from .gitignore
- Remove repo-mem / substrate_score / repo_search references from all .md
- Move 15 EVOLV skills from .agents/skills/ to .claude/skills/ so they are
auto-discovered by the Claude Code harness and invokable via the Skill tool
- Retire .agents/skills/evolv-orchestrator (duplicate of the subagent at
.claude/agents/evolv-orchestrator.md); orchestrator lives as a subagent only
- Drop OpenAI-format agent yaml metadata from each skill (not needed for CC)
- Update CLAUDE.md, CONTRACTS.md, AGENTS.md to point at the new locations and
disambiguate skills (.claude/skills/) vs subagents (.claude/agents/)
- Fix CLAUDE.md tick-loop wording (opt-in per-node, not a fixed 1000ms)
- Widen .claude/rules/ paths frontmatter so node-architecture and telemetry
rules trigger on more relevant files; add frontmatter to flow-layout rule
- Bump CONTRACTS.md review date to 2026-05-19; add step 7 to the contract-
change workflow (review example flows when topic usage changes)
- Bump nodes/generalFunctions pin (Home.md substrate_score reference removed)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Establish CONTRACTS.md at the EVOLV root as the canonical map of where every
contract, rule, and standard lives. Surface it from CLAUDE.md so every fresh
agent or colleague lands there first.
Reshape .claude/refactor/ to reflect that the platform refactor is done:
live standards stay at the top level; the plan artifacts (CONTINUE_HERE.md,
TASKS.md) move into Archive/ with WARNING banners.
Drop content that drifted out of date or duplicated the new standards stack:
- docs/DEVELOPER_GUIDE.md (pre-refactor walkthrough; superseded by
wiki/Architecture, wiki/Getting-Started, .claude/rules/node-architecture,
.claude/refactor/MODULE_SPLIT + per-node CONTRACT.md + src/commands/).
- .agents/decisions/ (15 DECISION files): load-bearing decisions belong in
commit messages and PR descriptions; live open items in OPEN_QUESTIONS.md.
- .agents/improvements/TOP10_*.md: moved to Archive/.
Bump generalFunctions to 49c77f2 — adds CONTRACT.md inside the library:
different shape from per-node CONTRACT.md files (library API, not msg.topic),
with stability tags and pointers to .claude/refactor/CONTRACTS.md §N.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* generalFunctions @ 34a4ef0 — new iconHelpers module + initVisuals
step on logger/position menus; asset selector rebuilt as a chip
wizard with per-stage type-to-filter combobox and node-aware curve
mini-chart (rotatingMachine Q-H, valve Cv, diffuser SOTE).
* machineGroupControl @ 6833e9f — consumes the shared visuals;
strategy/rendezvous cards keep their MGC-local SVGs; maintenance
switched to FA fa-wrench. Output-format pickers now use the shared
.evolv-icon-picker classes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- machineGroupControl @ 472402c — rendezvous planner extracted into
src/movement/. Every dispatch (both optimalControl and priorityControl)
routes through a shared _dispatchFlowDistribution helper so all pumps
reach their setpoint at t* = max(eta_i) regardless of per-pump speed.
New "Same-time landing" toggle in the editor (planner.useRendezvous,
default true) for operators who want the legacy fire-and-forget.
- generalFunctions @ af02d36 — new planner.useRendezvous schema field
and stateManager.getRemainingTransitionS() that the planner reads to
compute exact eta for children mid-ladder.
- rotatingMachine @ 5ea0b0b — sequenceController honors the new
sequenceAbortToken so a pre-empted sequence (e.g. shutdown caught
mid-ramp by a fresh demand) cleanly breaks out instead of barging
through to its terminal state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- generalFunctions f8f71a4: schema additions (output.process/dbase,
functionality.distance, drop prioritypercentagecontrol), measurement
position.x nullable, asset-data file renamed machine.json ->
rotatingmachine.json so AssetMenu lookup matches, menu re-derives
supplier/assetType from saved model id on reopen.
- pumpingStation 2c7fe17: setDemand reads unit-normalised payload from
commandRegistry (mirrors today's MGC change to unit-self-describing
demand commands). Pre-existing test failure (stale path to
basic-dashboard.flow.json, renamed to 02-Dashboard.json in fe5fa35) is
unrelated to this commit.
- rotatingMachine 394a972: η = (Q·ΔP)/P_shaft replaces the legacy Q/P
formula — gives a real BEP peak so NCog stops collapsing to 0 and the
MGC dashboard's BEP-position metric actually moves. Asset-registry
lookup renamed machine -> rotatingmachine (matches generalFunctions
rename). Constructor stateConfig pass-through fixed (default-param was
clobbering BaseNodeAdapter's pre-set extras). + 2 new tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MGC submodule lands the 2026-05-14 governance review fixes plus rolled-up
session work: _output-manifest.md per the new output-coverage rule,
computeEqualFlowDistribution extracted as a pure function (testable without
MGC), groupEfficiency degenerate-case fix, unit-self-describing set.demand,
eta = (Q*dP)/P formula correction, and dashboard fan-out hardening
(auto-init, NCog normalization, Q-H trim, null-trap closure). Suite 108/108.
Superproject adds:
- .claude/rules/output-coverage.md: every-output-every-state testing rule
prompted by the eta-null crash earlier in the session.
- CLAUDE.md: pointer to the new rule under Conventions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to the AssetResolver landing. All five diffuser supplier
curves now share one X-axis convention; diffuser specificClass
computes specific flux from total flow + membrane area and queries the
curves at that flux. Each curve file carries its own
_meta.membraneArea_m2_per_element so the node defaults are correct
without any per-node overrides.
Supplier naming fixed: Sulzer (PIK300/PRK300), Aquaconsult-Entec
(Aerostrip Phoenix).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Coordinated cutover across five submodules to the generalFunctions
asset registry. Highlights:
- generalFunctions: AssetResolver namespace + FileBackend, with new
diffuser supplier curves (GVA migrated, Jäger JetFlex EPDM-1000,
Aerostrip Phoenix multi-coverage, PIK300/PRK300 multi-coverage).
Diffuser config schema corrected: density was always meant to be
bottom-coverage %, not elements/m².
- diffuser: _loadSpecs reads from the registry; editor wired with the
shared asset cascade (supplier → type → model → unit).
- rotatingMachine + valve: derive supplier/type/units from the model
id via resolveAssetMetadata; reject saved legacy fields with a clear
re-save prompt.
- machineGroupControl: integration fixtures use the trimmed asset
shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces mode/scaling in the editor, fixes the camelCase-vs-lowercase
mismatch that silently disabled dispatch on default config, compacts the
status badge, extends getOutput with capacity / machine-count fields, and
replaces the pre-refactor example stubs with 01-Basic.json (MGC + 3
pumps + setup) and 02-Dashboard.json (FlowFuse dashboard with charts).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>