diff --git a/CLAUDE.md b/CLAUDE.md
index 7ca4005d..6cc357ba 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -509,11 +509,13 @@ touching it:
- **Deferred, with reasons (do not read "seven observability topics" from the program sketch as the
delivered scope):** `redundancy-state` stays on DPS in every mode (pair-local, built from
`Cluster.State`, drives ServiceLevel + the Primary gate — genuinely mesh-bound by design, not an
- observability panel); `fleet-status` is central-internal (`FleetStatusBroadcaster` builds it from
- the admin node's own membership events, `Fleet.razor` polls the Config DB and ignores the feed —
- never a node→central stream); `deployment-acks` already rides the Phase 2 gRPC command transport
+ observability panel); `deployment-acks` already rides the Phase 2 gRPC command transport
(`MeshTransport:Mode=Grpc`, `CentralCommandService.ReportApply`) as a command-plane reply, not
- telemetry.
+ telemetry. A third, `fleet-status`, was **DELETED** rather than deferred (2026-08-06, Gitea #542):
+ `FleetStatusBroadcaster` → DPS → SignalR bridge → `FleetStatusHub` had **zero clients end to end**
+ (no browser JS, no `HubConnection` — `/fleet` polls `NodeDeploymentState` in the Config DB), its
+ `DriverHostStatusHeartbeat` input had no production sender, and Phase 6 left its "fleet" as just
+ central's own pair. Fleet visibility is `/fleet` + the four per-node telemetry dials above.
- **The node ALWAYS hosts and ALWAYS double-publishes, in both modes.** Every publish seam emits
into a node-local `ITelemetryLocalHub` **and** still publishes DPS, unconditionally; the gRPC
server binds whenever `Telemetry:GrpcListenPort > 0`, independent of `Telemetry:Mode`. Only
diff --git a/docs/Configuration.md b/docs/Configuration.md
index ea007057..1a05b5d4 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -275,11 +275,12 @@ discipline as `MeshTransport:Mode` and `ConfigSource:Mode`.
otherwise) — enforced by `TelemetryOptionsValidator`. **`Grpc` mode on either side requires a
non-empty `ApiKey`** — fail-closed, same as `ConfigServe`/`LocalDb:Replication`.
-**Three DPS observability topics stay out of scope for this transport, on purpose:**
+**Two DPS observability topics stay out of scope for this transport, on purpose:**
`redundancy-state` (pair-local, built from `Cluster.State`, stays on DPS in both modes — see
-[`Redundancy.md`](Redundancy.md#command-transport-centralnode)), `fleet-status` (central-internal;
-`Fleet.razor` polls the Config DB, not any stream), and `deployment-acks` (already rides the Phase 2
-gRPC command transport, `CentralCommandService.ReportApply`, as a command-plane reply). See
+[`Redundancy.md`](Redundancy.md#command-transport-centralnode)) and `deployment-acks` (already rides
+the Phase 2 gRPC command transport, `CentralCommandService.ReportApply`, as a command-plane reply).
+A third, `fleet-status`, was **deleted** rather than deferred (2026-08-06, Gitea #542 — it had no
+client end to end; `Fleet.razor` polls the Config DB). See
[`docs/Telemetry.md`](Telemetry.md) for the full architecture, the reconnect/generation-stamped
dialer story, and the rationale for the node-hosts/central-dials inversion.
diff --git a/docs/Telemetry.md b/docs/Telemetry.md
index c7ce24f9..f873e8e6 100644
--- a/docs/Telemetry.md
+++ b/docs/Telemetry.md
@@ -78,7 +78,6 @@ What actually changes per mode, precisely:
`IDriverStatusSnapshotStore`, `IDriverResilienceStatusStore`).
- **The AdminUI panels themselves are untouched.** `/alerts`, `/script-log`, and the `/hosts`
driver table all read from the same sinks in both modes — only the sinks' upstream feed swaps.
- The `fleet-status` bridge (out of Phase 5 scope — see below) stays on DPS in both modes.
Because hosting is unconditional and both ingest paths are always compiled in, a fleet can be
flipped node-by-node and central-by-central with no coordination window where telemetry is lost —
@@ -130,10 +129,10 @@ Three things to know before touching it:
and no reader — the only reference in the repo is its DbSet declaration — while the live data reaches
the AdminUI over the `driver-resilience-status` channel above. Keep-or-delete is Gitea **#524**.
-## The three deferred channels (NOT migrated in Phase 5 — do not read this as "seven done")
+## The three non-migrated channels (NOT migrated in Phase 5 — do not read this as "seven done")
The program sketch originally named seven observability topics for Phase 5. Three were scoped out,
-each for a distinct, settled reason:
+each for a distinct, settled reason — and one of those three has since been deleted outright:
- **`redundancy-state`** — bidirectional, built directly from `Cluster.State`, and **pair-local**:
it drives ServiceLevel and the Primary data-plane gate, consumed in-process by
@@ -143,11 +142,17 @@ each for a distinct, settled reason:
in-mesh. Central's *display* of each pair's redundancy state is a Phase 6 cross-mesh concern
(a future observability channel, once central no longer shares gossip with any site pair), not a
Phase 5 telemetry-panel migration.
-- **`fleet-status`** — **central-internal**, not a node→central stream at all. The admin singleton
- `FleetStatusBroadcaster` builds it from the admin node's own cluster membership/reachability/
- leader events, and `Fleet.razor` **polls the Config DB** and ignores the feed entirely. There is
- nothing here for a per-node gRPC stream to carry. Revisit in Phase 6, once central genuinely
- loses gossip visibility of site nodes and needs another way to know a pair's membership.
+- **`fleet-status`** — **DELETED, 2026-08-06, Gitea #542.** There is no `fleet-status` channel any
+ more, on DPS or anywhere else. It was central-internal rather than a node→central stream: the
+ admin singleton `FleetStatusBroadcaster` built it from the admin node's own cluster membership/
+ reachability/leader events and published to a SignalR hub that **no client anywhere ever
+ connected to** — `Fleet.razor` polls `NodeDeploymentState` in the Config DB and always ignored
+ the feed. Its second input, `DriverHostStatusHeartbeat`, had no production sender either. Phase 6
+ then made the central-only view wrong as well (central's gossip ring is its own pair). The whole
+ chain — broadcaster, singleton registration, DPS topic, SignalR bridge, `FleetStatusHub`, and the
+ `FleetStatusChanged`/`FleetNodeStatus` messages — was removed rather than rewritten cross-mesh.
+ Fleet visibility that does work today: `/fleet` over `NodeDeploymentState`, and the four Phase 5
+ telemetry channels above, which are per-node dials and therefore already mesh-independent.
- **`deployment-acks`** — already migrated, but onto a different transport: it rides the Phase 2
gRPC command transport (`MeshTransport:Mode=Grpc`, `CentralCommandService.ReportApply`) as a
command-plane reply, not an observability broadcast. It was never a Phase 5 candidate.
diff --git a/scripts/compliance/phase-6-1-compliance.ps1 b/scripts/compliance/phase-6-1-compliance.ps1
index b2fa0e0b..eea2afcf 100644
--- a/scripts/compliance/phase-6-1-compliance.ps1
+++ b/scripts/compliance/phase-6-1-compliance.ps1
@@ -109,7 +109,8 @@ Write-Host ""
Write-Host "Stream E - Admin /hosts (data layer)"
Assert-FileExists "DriverInstanceResilienceStatus entity" "src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Entities/DriverInstanceResilienceStatus.cs"
Assert-FileExists "DriverResilienceStatusTracker present" "src/Core/ZB.MOM.WW.OtOpcUa.Core/Resilience/DriverResilienceStatusTracker.cs"
-Assert-Deferred "FleetStatusHub SignalR push + Blazor /hosts column refresh" "Phase 6.1 Stream E.2/E.3 visual-compliance follow-up"
+# Gitea #542 (2026-08-06): the FleetStatusHub chain was DELETED (dead end-to-end), so the
+# Stream E.2/E.3 "SignalR push + /hosts column refresh" follow-up is retired, not deferred.
Write-Host ""
Write-Host "Cross-cutting"
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Admin/StartDeploymentResult.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Admin/StartDeploymentResult.cs
index 8e72a5db..2c7fe3ec 100644
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Admin/StartDeploymentResult.cs
+++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Admin/StartDeploymentResult.cs
@@ -13,7 +13,7 @@ public enum StartDeploymentOutcome
///
/// Reply from the AdminOperationsActor singleton. Accepted means the snapshot
/// was sealed and a Deployment row was created; the in-flight deployment can be
-/// tracked through fleet-status broadcasts.
+/// tracked through the per-node NodeDeploymentState rows the AdminUI's /fleet page reads.
///
public sealed record StartDeploymentResult(
StartDeploymentOutcome Outcome,
diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Fleet/FleetStatusChanged.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Fleet/FleetStatusChanged.cs
deleted file mode 100644
index e5095e98..00000000
--- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/Messages/Fleet/FleetStatusChanged.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using ZB.MOM.WW.OtOpcUa.Commons.Types;
-
-namespace ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
-
-public enum FleetNodeHealth { Healthy, Degraded, Unreachable }
-
-public sealed record FleetNodeStatus(
- NodeId NodeId,
- FleetNodeHealth Health,
- RevisionHash? CurrentRevision,
- DateTime LastSeenUtc);
-
-///
-/// Periodic fleet-wide status broadcast pushed by FleetStatusBroadcaster to admin UI
-/// subscribers via SignalR.
-///
-public sealed record FleetStatusChanged(
- IReadOnlyList Nodes,
- DeploymentId? CurrentDeployment,
- DateTime AsOfUtc,
- CorrelationId CorrelationId);
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs
index dfd1a4b1..16c7271f 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs
@@ -9,8 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
///
/// Akka actor that subscribes to the alerts DistributedPubSub topic and forwards each
/// to every SignalR client connected to .
-/// Mirrors FleetStatusSignalRBridge's design — one bridge per admin node, hub fan-out is
-/// per-node, no cluster-singleton needed.
+/// One bridge per admin node — hub fan-out is per-node, so no cluster-singleton is needed.
///
public sealed class AlertSignalRBridge : ReceiveActor
{
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/FleetStatusHub.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/FleetStatusHub.cs
deleted file mode 100644
index 07b5f0f3..00000000
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/FleetStatusHub.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.SignalR;
-using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
-
-namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
-
-///
-/// Browser-facing fleet-status push channel. Subscribers receive
-/// snapshots whenever the admin-role FleetStatusBroadcaster publishes a diff.
-///
-/// Server pushes fleet-status updates to connected clients via FleetStatusSignalRBridge
-/// (DistributedPubSub 'fleet-status' → IHubContext<FleetStatusHub>).
-///
-[Authorize]
-public sealed class FleetStatusHub : Hub
-{
- public const string Endpoint = "/hubs/fleet-status";
- public const string MethodName = "fleetStatusChanged";
-}
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/FleetStatusSignalRBridge.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/FleetStatusSignalRBridge.cs
deleted file mode 100644
index 3b48963c..00000000
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/FleetStatusSignalRBridge.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using Akka.Actor;
-using Akka.Cluster.Tools.PublishSubscribe;
-using Akka.Event;
-using Microsoft.AspNetCore.SignalR;
-using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
-
-namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
-
-///
-/// Akka actor that subscribes to the fleet-status DistributedPubSub topic and forwards
-/// every snapshot to all SignalR clients connected to
-/// . Spawned on admin-role nodes by
-/// AddOtOpcUaSignalRBridges.
-///
-/// The bridge runs locally on each admin node — every node that hosts the hub also forwards
-/// snapshots to its own connected clients. That keeps the hub-to-actor wiring simple (no
-/// cluster-singleton coordination needed) at the cost of duplicated DPS subscriptions on
-/// multi-admin deployments. Acceptable since the messages are small + the SignalR fan-out
-/// is per-node anyway.
-///
-public sealed class FleetStatusSignalRBridge : ReceiveActor
-{
- public const string TopicName = "fleet-status";
-
- private readonly IHubContext _hub;
- private readonly ILoggingAdapter _log = Context.GetLogger();
-
- /// Creates actor props for a FleetStatusSignalRBridge with the specified hub context.
- /// The SignalR hub context.
- /// Props for creating a new FleetStatusSignalRBridge actor.
- public static Props Props(IHubContext hub) =>
- Akka.Actor.Props.Create(() => new FleetStatusSignalRBridge(hub));
-
- /// Initializes a new instance of the FleetStatusSignalRBridge class.
- /// The SignalR hub context for sending messages to clients.
- public FleetStatusSignalRBridge(IHubContext hub)
- {
- _hub = hub;
- ReceiveAsync(ForwardAsync);
- Receive(_ => { /* DPS confirmation */ });
- }
-
- ///
- protected override void PreStart() =>
- DistributedPubSub.Get(Context.System).Mediator.Tell(new Subscribe(TopicName, Self));
-
- private async Task ForwardAsync(FleetStatusChanged msg)
- {
- try
- {
- await _hub.Clients.All.SendAsync(FleetStatusHub.MethodName, msg);
- }
- catch (Exception ex)
- {
- _log.Warning(ex, "FleetStatusSignalRBridge: SignalR push failed (count={Count})", msg.Nodes.Count);
- }
- }
-}
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs
index 1013b021..c5f3cb4b 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubRouteBuilderExtensions.cs
@@ -13,7 +13,6 @@ public static class HubRouteBuilderExtensions
/// The same endpoint route builder, for chaining.
public static IEndpointRouteBuilder MapOtOpcUaHubs(this IEndpointRouteBuilder app)
{
- app.MapHub(FleetStatusHub.Endpoint);
app.MapHub(AlertHub.Endpoint);
app.MapHub(ScriptLogHub.Endpoint);
app.MapHub(DriverStatusHub.Endpoint);
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs
index cf664c67..9e3c7f24 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs
@@ -14,7 +14,6 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
public static class HubServiceCollectionExtensions
{
- public const string FleetStatusSignalRBridgeName = "fleet-status-signalr-bridge";
public const string AlertSignalRBridgeName = "alert-signalr-bridge";
public const string ScriptLogSignalRBridgeName = "script-log-signalr-bridge";
public const string DriverStatusSignalRBridgeName = "driver-status-signalr-bridge";
@@ -44,8 +43,8 @@ public static class HubServiceCollectionExtensions
///
/// Spawns the SignalR bridge actors that forward DPS messages to browser-facing SignalR
- /// hubs: fleet-status → , alerts →
- /// , script-logs → .
+ /// hubs: alerts → , script-logs →
+ /// , driver-health → .
///
/// Call inside the admin-role configurator on the shared :
///
@@ -62,12 +61,6 @@ public static class HubServiceCollectionExtensions
{
builder.WithActors((system, registry, resolver) =>
{
- // Fleet-status always stays on DPS in both modes (deferred / out of Phase 5 scope) — it is
- // never gated by the telemetry dark switch.
- var fleetHub = resolver.GetService>();
- var fleetBridge = system.ActorOf(FleetStatusSignalRBridge.Props(fleetHub), FleetStatusSignalRBridgeName);
- registry.Register(fleetBridge);
-
// The four telemetry sinks are registered identically in both modes; only the upstream that
// feeds them swaps (DPS bridges vs. the gRPC dial supervisor). Resolve them once, above the
// branch, so both paths feed the SAME singletons the Blazor panels read.
@@ -131,7 +124,6 @@ public static class HubServiceCollectionExtensions
}
/// Marker keys for lookup of the SignalR bridge actors.
-public sealed class FleetStatusSignalRBridgeKey { }
public sealed class AlertSignalRBridgeKey { }
public sealed class ScriptLogSignalRBridgeKey { }
public sealed class DriverStatusSignalRBridgeKey { }
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Fleet/FleetStatusBroadcaster.cs b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Fleet/FleetStatusBroadcaster.cs
deleted file mode 100644
index b56520f8..00000000
--- a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Fleet/FleetStatusBroadcaster.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-using Akka.Actor;
-using Akka.Cluster;
-using Akka.Cluster.Tools.PublishSubscribe;
-using Akka.Event;
-using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
-using ZB.MOM.WW.OtOpcUa.Commons.Types;
-
-namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Fleet;
-
-///
-/// Admin-role cluster singleton that maintains an in-memory
-/// snapshot from cluster membership + reachability events plus per-node heartbeats, and pushes
-/// diffs on the fleet-status DistributedPubSub topic. The SignalR hub layer
-/// subscribes to that topic and forwards to browser clients.
-///
-/// Heartbeat staleness: a node we haven't heard from in flips
-/// from to . A
-/// cluster Unreachable event flips it straight to .
-///
-public sealed class FleetStatusBroadcaster : ReceiveActor, IWithTimers
-{
- public const string Topic = "fleet-status";
- public static readonly TimeSpan BroadcastInterval = TimeSpan.FromSeconds(5);
- public static readonly TimeSpan HeartbeatTimeout = TimeSpan.FromSeconds(30);
-
- private readonly Akka.Cluster.Cluster _cluster;
- private readonly Action