Replicated the Modbus #218 bring-up against the AB CIP + S7 seeds to
confirm the factories + seeds shipped in #217 actually work end-to-end.
Both pass 5/5 e2e stages with `OpcUaServer:AnonymousRoles=[WriteOperate]`
(the #221 knob).
## AB CIP (against ab_server controllogix fixture, port 44818)
```
=== AB CIP e2e summary: 5/5 passed ===
[PASS] Probe
[PASS] Driver loopback
[PASS] Server bridge (driver → server → client)
[PASS] OPC UA write bridge (client → server → driver)
[PASS] Subscribe sees change
```
Server log: `DriverInstance abcip-smoke-drv (AbCip) registered +
initialized` ✓.
## S7 (against python-snap7 s7_1500 fixture, port 1102)
```
=== S7 e2e summary: 5/5 passed ===
[PASS] Probe
[PASS] Driver loopback
[PASS] Server bridge
[PASS] OPC UA write bridge
[PASS] Subscribe sees change
```
Server log: `DriverInstance s7-smoke-drv (S7) registered + initialized` ✓.
## Seed fixes so bring-up is idempotent
Live-boot exposed two seed-level papercuts when applying multiple
smoke seeds in sequence:
1. **SA credential collision.** `UX_ClusterNodeCredential_Value` is a
unique index on `(Kind, Value) WHERE Enabled=1`, so `sa` can only
bind to one node at a time. Each seed's DELETE block only dropped
the credential tied to ITS node — seeding AbCip after Modbus blew
up with `Cannot insert duplicate key` on the sa binding. Added a
global `DELETE FROM dbo.ClusterNodeCredential WHERE Kind='SqlLogin'
AND Value='sa'` before the per-cluster INSERTs. Production deployments
using non-SA logins aren't affected.
2. **DashboardPort 5000 → 15050.** `HealthEndpointsHost` uses
`HttpListener`, which rejects port 5000 on Windows without a
`netsh http add urlacl` grant or admin rights. 15050 is unreserved
+ loopback-safe per the HealthEndpointsHost remarks. Applied to all
four smoke seeds (Modbus was patched at runtime in #218; now baked
into the seed).
## AB Legacy status
Not live-boot verified — ab_server PCCC dispatcher is upstream-broken
(#222). The factory + seed ship ready for hardware; the seed's DELETE
+ DashboardPort fixes land in this PR so when real SLC/MicroLogix/PLC-5
arrives the sql just applies.
## Closes#220
Umbrella #209 was already closed; #220 was the final child.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>