Verified: Consuming a Sparkplug fleet
Each claim the Consuming a Sparkplug fleet page makes, with the tests that
prove it in the last green CI run: 40 of 40 verified.
The claims live in docs/claims/sparkplug-host.yaml.
Last green CI run 16 Oct 2026, 14:48 UTC on c7618d3 · all runtime claims
SPH-001 verified
`broker`, `host-id`, `group-id` (or `group-ids`) and `manifest` are required for a sparkplug-host driver; a project missing one fails `naut check` (at load), not `naut run`.
- pass
TestLoadSparkplugHostErrorsininternal/project(7 of 7 passed) - pass
TestSchemaRequiresWhatLoaderRequiresininternal/project - pass
TestSparkplugHostGroupIDsAloneininternal/project
SPH-002 verified
`New` never dials: it builds the whole driver (manifest, indexes, companion tags) offline, so a complete sparkplug-host project passes `naut check` with no broker in sight.
- pass
TestDriverNewNeverDialsinsparkplug/host - pass
TestLoadSparkplugHostDriverininternal/project - pass
TestCheckSparkplugHostProjectOfflineincmd/naut
SPH-003 verified
The broker password is never read from the manifest: it comes from `NAUTILUS_MQTT_PASSWORD`.
- pass
TestSparkplugHostPasswordComesFromTheEnvironmentininternal/project(3 of 3 passed)
SPH-004 verified
`primary` defaults to true: a host project that does not set it publishes STATE.
- pass
TestSparkplugHostPrimaryDefaultsToTrueininternal/project
SPH-005 verified
The primary host publishes STATE on `spBv1.0/STATE/<host-id>`, `state-form: both` also publishes the legacy `STATE/<host-id>` form, and a passive consumer (`primary: false`) publishes no STATE.
- pass
TestMqttStateBirthMatchesWillinsparkplug/host - pass
TestMqttStateFormBothPublishesLegacyinsparkplug/host - pass
TestMqttPassiveConsumerPublishesNoStateinsparkplug/host - pass
TestTCKHostConformanceinsparkplug/host
SPH-006 verified
`Stop` publishes the STATE death certificate before disconnecting.
- pass
TestMqttStopPublishesStateDeathinsparkplug/host - pass
TestTCKHostConformanceinsparkplug/host
SPH-007 verified
`naut sparkplug import --sites` generates the types, manifest and tag file offline from a committed site list, identical for the metrics it shares with the live (from-births) path.
- pass
TestSparkplugImportFromSitesincmd/naut - pass
TestFromSitesMatchesFromBirthsinsparkplug/host/codegen
SPH-008 verified
Live import asks every node heard from to rebirth (births are not retained) and generates from what birthed.
- pass
TestDiscoverAsksForRebirthinsparkplug/host - pass
TestSparkplugImportFromBrokerAsksForRebirthincmd/naut
SPH-010 verified
`naut sparkplug import` writes Sparkplug Templates as ST `TYPE`s in `sparkplug_types.st`, composed automatically as a library so a program can use them with no entry in `nautilus.yaml`.
- pass
TestSparkplugImportFromSitesincmd/naut - pass
TestGenerateGoldeninsparkplug/host/codegen - pass
TestCheckSparkplugHostProjectOfflineincmd/naut
SPH-011 verified
`naut sparkplug tags` re-derives the tag file from a committed manifest with no broker.
- pass
TestTagsRoundTripinsparkplug/host/codegen - pass
TestSparkplugImportFromSitesincmd/naut
SPH-012 verified
`naut sparkplug browse` prints what is on the wire without generating anything.
- pass
TestSparkplugBrowsePrintsWithoutGeneratingincmd/naut
SPH-013 verified
The driver subscribes to the group and presents every edge node's (and device's) data as `role: input` tags.
- pass
TestStateNBirthAppliesValuesinsparkplug/host - pass
TestStateAliasOnlyNDatainsparkplug/host - pass
TestStateDeviceLifecycleinsparkplug/host - pass
TestMqttNBirthReachesReadInputsinsparkplug/host
SPH-014 verified
The companions exist per site and device: `<site>__Online` is true from NBIRTH to the matching NDEATH, `<site>_<device>__Online` does the same per device, and `<site>__LastBirthMs` is the epoch ms of the last NBIRTH.
- pass
TestStateNBirthAppliesValuesinsparkplug/host - pass
TestStateNDeathKeepsValuesinsparkplug/host - pass
TestStateStaleNDeathIgnoredinsparkplug/host - pass
TestEdgeBrokerDropIsANewSessionToTheHostinsparkplug/host - pass
TestStateDeviceLifecycleinsparkplug/host - pass
TestStateLastBirthMsMovesToTheNewerBirthinsparkplug/host - pass
TestCompanionTagNamesinsparkplug/host - pass
TestBuildIndexesSynthesizesDefaultCompanionsinsparkplug/host
SPH-015 verified
The driver never invents or zeroes a value on death: an NDEATH keeps every value (and takes the node and its devices offline), and a DDEATH keeps the device's values.
- pass
TestStateNDeathKeepsValuesinsparkplug/host - pass
TestStateDeviceLifecycleinsparkplug/host - pass
TestEdgeToHostDogfoodinsparkplug/host
SPH-016 verified
A rising edge on the `<site>__Rebirth` output sends NCMD Node Control/Rebirth to that site; a false value sends nothing.
- pass
TestMqttRebirthTagRisingEdgeinsparkplug/host
SPH-017 verified
A data tag stays absent from the driver's snapshot until its site has birthed (so reading it faults the scan), while `__Online` is present from t=0 as false.
- pass
TestStateNBirthAppliesValuesinsparkplug/host - pass
TestSparkplugHostAbsentTagFaultsTheScanUntilBirthininternal/project
SPH-018 verified
`Quality()` reports non-Good entries only: a binding never delivered (site never birthed, or the birth omits that metric) is NotConnected, and a binding whose site is not online (NDEATH, silence past `stale-after`, or DDEATH for a device-scoped metric) is Stale, scoped to the node or device concerned.
- pass
TestQualityPreBirthNotConnectedinsparkplug/host - pass
TestQualityBirthEmptyMapinsparkplug/host - pass
TestQualityNDeathStaleScopedToNodeinsparkplug/host - pass
TestQualityDeviceDeathStaleJustThatDeviceinsparkplug/host - pass
TestQualityStaleSweepMarksStaleinsparkplug/host - pass
TestQualityMissingMetricFromBirthinsparkplug/host
SPH-019 verified
Writable bindings and the companions are host-authored and get no quality verdict.
- pass
TestQualityPreBirthNotConnectedinsparkplug/host - pass
TestQualityStaleSweepMarksStaleinsparkplug/host
SPH-020 verified
Silence longer than `stale-after` marks a node stale without disturbing its values.
- pass
TestStateSweepStaleinsparkplug/host - pass
TestQualityStaleSweepMarksStaleinsparkplug/host - pass
TestSparkplugHostStaleAfterReachesTheDriverininternal/project
SPH-021 verified
Output writes leave as NCMD (node metrics) or DCMD (device metrics), QoS 0, not retained, with full metric names (never aliases).
- pass
TestMqttCommandsLeaveAtQoS0NotRetainedUnderFullNamesinsparkplug/host - pass
TestMqttWriteOutputsSendsDCMDinsparkplug/host - pass
TestEdgeToHostDogfoodinsparkplug/host
SPH-022 verified
A command to a dark site is held (the latest value per tag) and delivered once on the site's next NBIRTH/DBIRTH, after the birth has settled what the site holds.
- pass
TestMqttWriteToOfflineNodeIsQueuedinsparkplug/host - pass
TestMqttQueuedWriteIsDeliveredOnBirthinsparkplug/host - pass
TestEdgeToHostQueuedWriteReachesAWokenSiteinsparkplug/host - pass
TestMqttChangePushCadenceinsparkplug/host - pass
TestQueueOfflineIsBoundedPerNodeinsparkplug/host
SPH-023 verified
If the site comes back already holding the commanded value, nothing goes on the wire; otherwise one command goes out, only for the members the operator touched.
- pass
TestMqttQueuedWriteTheSiteAlreadyHoldsIsNotSentinsparkplug/host - pass
TestEdgeToHostQueuedWriteReachesAWokenSiteinsparkplug/host
SPH-024 verified
The offline queue holds at most 256 tags per site, and a write past a full queue is counted as `WriteDrops`.
- pass
TestQueueOfflineIsBoundedPerNodeinsparkplug/host - pass
TestMqttQueueOverflowIsCountedAsWriteDropsinsparkplug/host
SPH-025 verified
`Status` counts `WriteQueued`, `QueuedWrites` per site, and a "queued writes" metric on `/api/drivers`.
- pass
TestQueueOfflineIsBoundedPerNodeinsparkplug/host - pass
TestMqttWriteToOfflineNodeIsQueuedinsparkplug/host - pass
TestMqttQueuedWritesAreCountedPerSiteinsparkplug/host - pass
TestSparkplugHostQueuedWritesAreReportedOnAPIDriversininternal/project
SPH-026 verified
A UDT is never writable as a whole: a `--writable` glob that matches a Template metric is a hard error, and controls are bound per member.
- pass
TestWritableWholeTemplateIsAnErrorinsparkplug/host/codegen - pass
TestValidateMemberBindingsinsparkplug/host
SPH-027 verified
A member binding is a scalar tag carrying the leaf member's type (named `<prefix>_<metric>_<member path>`), is output-only (`writable: true` required), with `member:` a dotted path into the type shape; reads keep coming from the parent struct tag.
- pass
TestValidateMemberBindingsinsparkplug/host - pass
TestValidateErrors/member binding must be writableinsparkplug/host - pass
TestStateTemplateAssemblyAndPartialUpdateinsparkplug/host - pass
TestWritableMemberGlobsinsparkplug/host/codegen - pass
TestSitesWritableMemberListinsparkplug/host/codegen
SPH-028 verified
Writing a member publishes a partial template naming the parent metric and carrying only that member; two members of one UDT in one coalesce window go out as one partial template, and nested paths build intermediate templates with their TemplateRef.
- pass
TestMqttWriteMemberSendsPartialTemplateinsparkplug/host - pass
TestMqttWriteNestedMembersCoalesceinsparkplug/host
SPH-029 verified
A partial template written by the host merges member by member into nautilus's own edge, so members the host did not write are kept.
- pass
TestCommandPartialTemplateKeepsOtherMembersinsparkplug - pass
TestCommandNestedPartialTemplateinsparkplug - pass
TestEdgeToHostQueuedWriteReachesAWokenSiteinsparkplug/host
SPH-030 verified
The first output snapshot after `Start` is a baseline that is recorded and published to nobody, so a host connecting to an online fleet never zeroes an edge's setpoints.
- pass
TestMqttStartCommandsNothinginsparkplug/host - pass
TestEdgeToHostStartKeepsSetpointsinsparkplug/host - pass
TestMqttChangePushCadenceinsparkplug/host
SPH-031 verified
After the baseline only a value that actually moved is a command, published once however many scans hand it back.
- pass
TestMqttOperatorWritePublishesOnceinsparkplug/host - pass
TestMqttChangePushCadenceinsparkplug/host
SPH-032 verified
A member output adopts the site's live value when its parent template arrives (NBIRTH, DBIRTH, NDATA or rebirth): writing the value the panel already reports sends nothing, anything else sends one partial template; before the first birth an operator write still publishes.
- pass
TestMqttMemberBaselineAdoptsLiveValueinsparkplug/host - pass
TestEdgeToHostMemberBaselineAdoptsLiveValueinsparkplug/host - pass
TestMemberOutputAdoptsTheLiveValueOnEveryTriggerinsparkplug/host - pass
TestEdgeToHostQueuedWriteReachesAWokenSiteinsparkplug/host
SPH-033 verified
A reconnect replays nothing: change detection survives a dropped connection, while commands raised during the outage are queued and go out on reconnect.
- pass
TestMqttReconnectDoesNotReplayOutputsinsparkplug/host - pass
TestMqttCommandRaisedDuringAnOutageGoesOutOnReconnectinsparkplug/host
SPH-034 verified
`__Rebirth` follows the baseline rule: only a rising edge is a command, and a `__Rebirth` already TRUE on the first scan is baseline, so a value held TRUE across a restart does not rebirth every site.
- pass
TestMqttRebirthTagRisingEdgeinsparkplug/host - pass
TestMqttRebirthTagTrueInFirstSnapshotinsparkplug/host
SPH-035 verified
`/api/drivers` reports Kind `sparkplug-host`, Name the host-id, state error (broker down), connecting (no births yet), degraded (any node offline or strict-mode unknowns) or connected, with one device row per edge node and device sub-rows flattened as `W6/plc1`.
- pass
TestHostStatusStatesininternal/project - pass
TestHostStatusDeviceRowsininternal/project
SPH-036 verified
With a `redundancy:` section only the leader may claim to be online: the STATE publish and every outbound NCMD/DCMD are gated on leadership, not just on `primary: true`. On promotion the new leader publishes STATE online; on demotion the old one stops commanding and publishes its STATE death. Without `redundancy:` the host is always the leader.
- pass
TestMqttStandbyPublishesNoStateAndNoCommandsUntilLeaderinsparkplug/host - pass
TestWriteGateDrivesStateAndCommandsinsparkplug/host - pass
TestWriteGateLeaderReassertsStateOverAStaleDeathinsparkplug/host - pass
TestNoWriteGateSingleHostIsAlwaysPrimaryinsparkplug/host - pass
TestRedundantSparkplugHostOnlyTheLeaderIsPrimaryincmd/naut - pass
TestRedundantSparkplugHostOnABenchIsPrimaryincmd/naut - pass
TestSingleSparkplugHostWithoutRedundancyIsAlwaysPrimaryincmd/naut
SPH-037 verified
The host passes the Sparkplug TCK host-application profile (NBIRTH, DBIRTH, NDATA, a deliberate sequence gap, DDEATH, NDEATH) with a non-zero PASS count and zero FAIL.
- pass
TestTCKHostConformanceinsparkplug/host
SPH-038 verified
A sequence gap left unfilled for `reorder-timeout` sends NCMD Rebirth to the node; a gap filled inside the window drains without one.
- pass
TestStateSeqGapRequestsRebirthinsparkplug/host - pass
TestStateOutOfOrderFillInDrainsinsparkplug/host - pass
TestTCKHostConformanceinsparkplug/host
SPH-041 verified
Metrics the manifest never bound are handled by `on-unknown` (`log`, `ignore` or `strict`); under `strict` the driver reports degraded.
- pass
TestStateUnknownMetricDiscoveryinsparkplug/host - pass
TestStateDiscoveryPoliciesinsparkplug/host(4 of 4 passed) - pass
TestStateUnknownMetricLoggingFollowsOnUnknowninsparkplug/host(4 of 4 passed) - pass
TestHostStatusStatesininternal/project - pass
TestSparkplugHostOnUnknownReachesTheDriverininternal/project(4 of 4 passed)
SPH-042 verified
Only the configured group(s) are consumed from the broker.
- pass
TestStateGroupFilterinsparkplug/host
SPH-043 verified
`Start` owns the connection and its reconnect loop; the driver reconnects after a broker restart.
- pass
TestMqttReconnectsAfterBrokerRestartinsparkplug/host