Files
scadaproj/ZB.MOM.WW.Health/CLAUDE.md
T
Joseph Doherty 6a4fa220de docs(health): oldest-Up-member is the active-node rule (0.3.0)
Rewrites the active-node sections of the spec and shared contract, which
described the leader-based design that both consumers rejected.

- SPEC §2.3 replaced: the rule, the ordered RolePreference, why a node holding
  none of the preferred roles is Unhealthy rather than Healthy, and the data
  payload. §4 (IActiveNodeGate) and the tier table follow.
- SPEC §6 marked historical: it still tells adopters to use
  ActiveNodeHealthCheck(role: "admin") and AkkaActiveNodeGate, which is the
  advice that produced the defect. Kept for provenance, annotated so nobody
  follows it again.
- GAPS: gaps L1/L2 recorded as closed the wrong way, reopened, then closed at
  0.3.0 — including the part worth remembering, that both apps adopted the
  shared check as specified, hit it live, and reverted to private copies, so the
  surface reached zero consumers by being avoided rather than unused.
- Index rows updated with the consumer-visible changes and both app branches.
2026-07-24 13:24:30 -04:00

4.0 KiB

ZB.MOM.WW.Health

Health-check libraries for the ZB.MOM.WW SCADA family (OtOpcUa, MxAccessGateway, ScadaBridge). These are libraries, not a service — each package is linked directly into the consuming application at build time. There is no central health process or network hop; probes run in-process alongside the application.

The library normalizes the three-tier health endpoint convention (/health/ready, /health/active, /healthz) and provides reusable probe implementations so the three sister projects share a common surface without duplicating probe logic.

Built at 0.3.0 and published to the Gitea NuGet feed. Adopted by all four apps — adoption is tracked in ~/Desktop/scadaproj/components/health/GAPS.md.


Packages

Package Responsibilities Key Dependencies
ZB.MOM.WW.Health Core tier convention, MapZbHealth extension, canonical JSON writer (ZbHealthWriter), IActiveNodeGate seam, GrpcDependencyHealthCheck reachability probe, tier-tag constants (ZbHealthTags). No Akka or EF dependency. Microsoft.AspNetCore.App (framework ref), Grpc.Net.Client
ZB.MOM.WW.Health.Akka AkkaClusterHealthCheck with a configurable AkkaClusterStatusPolicy (presets: Default / OtOpcUaCompat); ClusterActiveNode — the family-wide oldest-Up-member rule, optionally role-scoped; ActiveNodeHealthCheck over it (role-preference list, activeRole/activeNode in the result data); and AkkaActiveNodeGate, which backs IActiveNodeGate from the same rule. ZB.MOM.WW.Health, Akka.Cluster
ZB.MOM.WW.Health.EntityFrameworkCore DatabaseHealthCheck<TContext> — resolves IDbContextFactory<TContext> when registered, else a scoped TContext; pool-safe. Default probe: CanConnectAsync. Optional ProbeQuery delegate for query-based validation. ZB.MOM.WW.Health, Microsoft.EntityFrameworkCore

Consumer matrix

Consumer ZB.MOM.WW.Health (core) ZB.MOM.WW.Health.Akka ZB.MOM.WW.Health.EntityFrameworkCore
OtOpcUa yes yes yes
MxAccessGateway yes
ScadaBridge yes yes yes

MxAccessGateway consumes the core package only — it has no Akka cluster and no EF DbContext. OtOpcUa and ScadaBridge consume all three packages.


Build, test, and pack commands

# From ZB.MOM.WW.Health/

# Build
dotnet build ZB.MOM.WW.Health.slnx
dotnet build ZB.MOM.WW.Health.slnx -c Release

# Test (no external dependencies — no running Akka cluster, no database)
dotnet test ZB.MOM.WW.Health.slnx

# Pack (three .nupkg files land in artifacts/)
dotnet pack ZB.MOM.WW.Health.slnx -c Release -o ./artifacts

All three test assemblies run offline. The Akka suite forms real in-process clusters on loopback: a single-node one for the cluster-data payload, and a two-node one built so the oldest member is not the lowest-addressed one — the only condition under which age- and leader-ordering disagree, and so the only fixture that can catch a leader-based active-node regression. It binds fixed ports 19540/19541 (distinct from OtOpcUa's equivalent fixture) and carries a sanity check that the divergence actually occurred, so the assertions cannot pass for the wrong reason:

Assembly Tests
ZB.MOM.WW.Health.Tests 25
ZB.MOM.WW.Health.Akka.Tests 45
ZB.MOM.WW.Health.EntityFrameworkCore.Tests 6
Total 76

GeneratePackageOnBuild is off — pack explicitly with the command above.


Status

Built at 0.3.0 and published to the Gitea NuGet feed. Adopted by all four apps — adoption is tracked in the component backlog:

  • ~/Desktop/scadaproj/components/health/GAPS.md — adoption order, effort, and risk

Design documentation:

  • ~/Desktop/scadaproj/components/health/spec/SPEC.md — normalized three-tier target
  • ~/Desktop/scadaproj/components/health/shared-contract/ZB.MOM.WW.Health.md — proposed API (aligned to shipped code)
  • ~/Desktop/scadaproj/components/health/current-state/ — per-project current state (code-verified)