diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs
index 0624b88..c93c998 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateAlmdTests.cs
@@ -29,6 +29,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.Emulate;
/// Runs only when AB_SERVER_PROFILE=emulate. ab_server has no ALMD
/// instruction + no alarm subsystem, so this tier-gated class couldn't produce a
/// meaningful result against the default simulator.
+/// The Emulate tier is hardware-gated (Rockwell per-seat license, Windows-only,
+/// conflicts with Docker Desktop's WSL 2 backend) so a permanent skip in CI is expected
+/// — see docs/drivers/AbServer-Test-Fixture.md §"Logix Emulate golden-box tier"
+/// for the full rationale + the gap matrix this test closes.
///
[Collection("AbServerEmulate")]
[Trait("Category", "Integration")]
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs
index 3386629..9382579 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests/Emulate/AbCipEmulateUdtReadTests.cs
@@ -27,6 +27,10 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests.Emulate;
/// Runs only when AB_SERVER_PROFILE=emulate. With ab_server
/// (the default), skips cleanly — ab_server lacks UDT / Template Object emulation
/// so this wire-level test couldn't pass against it regardless.
+/// The Emulate tier is hardware-gated (Rockwell per-seat license, Windows-only,
+/// conflicts with Docker Desktop's WSL 2 backend) so a permanent skip in CI is expected
+/// — see docs/drivers/AbServer-Test-Fixture.md §"Logix Emulate golden-box tier"
+/// for the full rationale + the gap matrix this test closes.
///
[Collection("AbServerEmulate")]
[Trait("Category", "Integration")]
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md
index dca6e16..0986415 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md
@@ -48,6 +48,23 @@ service + starting another. The integration tests discriminate by a
separate `MODBUS_SIM_PROFILE` env var so they skip correctly when the
wrong profile is live.
+### Profile coverage matrix
+
+The two general-purpose profiles cover disjoint test sets. A full pass
+of the integration suite requires running both — serially on a single
+docker host (the `:5020` collision), or in parallel on two hosts.
+
+| Job | Bring up | Env to set | Expected outcome |
+|---|---|---|---|
+| `modbus-standard` | `lmxopcua-fix up modbus standard` | unset `MODBUS_SIM_PROFILE` (or set to `standard`) | Standard round-trip + AddressingGrammar suites pass; `ExceptionInjectionTests` (32 rows) skip with `MODBUS_SIM_PROFILE != exception_injection`. |
+| `modbus-exception` | `lmxopcua-fix up modbus exception_injection` | `MODBUS_SIM_PROFILE=exception_injection` | `ExceptionInjectionTests` (32 rows) pass against the per-`(fc,address)` rule set; standard-profile suites (round-trip, AddressingGrammar) skip. |
+
+The DL205 / Mitsubishi / S7-1500 profiles are similar — each gates its
+own quirks suite via `MODBUS_SIM_PROFILE=`. Tests that don't
+need a specific profile (the basic round-trip set) run under any of
+the three pymodbus-based profiles. The `exception_injection` profile
+is the only one that runs `exception_injector.py` instead of pymodbus.
+
## Endpoint
- Default: `localhost:5020`
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs
index 6c11859..c158e82 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Admin.E2ETests/AdminWebAppFactory.cs
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.OtOpcUa.Admin.Hubs;
+using ZB.MOM.WW.OtOpcUa.Admin.Security;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
@@ -101,6 +102,15 @@ public sealed class AdminWebAppFactory : IAsyncDisposable
builder.Services.AddScoped();
builder.Services.AddScoped();
+ // ClusterDetail.razor injects AdminHubConnectionFactory to drive the live-banner hub
+ // connection; the factory depends on HubTokenService, which in turn needs Data Protection.
+ // Without these the InteractiveServer circuit fails to instantiate the component and the
+ // page never advances past the "Loading…" placeholder — Playwright then times out
+ // waiting for any tab nav-link to appear. Mirrors Program.cs:35-36.
+ builder.Services.AddDataProtection();
+ builder.Services.AddSingleton();
+ builder.Services.AddScoped();
+
_app = builder.Build();
_app.UseStaticFiles();
_app.UseRouting();