Sixth PR of the alarms-over-gateway epic
(docs/plans/alarms-over-gateway.md). Depends on PR C.2 (sidecar
serves IAlarmEventWriter when enabled), already merged.
Today Phase7Composer.ResolveHistorianSink only scans drivers for an
IAlarmHistorianWriter — no Galaxy driver provides one since PR 7.2,
so the resolution falls through to NullAlarmHistorianSink and
scripted-alarm transitions are silently discarded.
WonderwareHistorianClient already implements IAlarmHistorianWriter
and Program.cs:178 already registers it as a singleton when
Historian:Wonderware:Enabled=true. The gap was that Phase7Composer
ignored DI: this PR adds an optional injectedWriter constructor
parameter, and ASP.NET Core DI resolves it from the same
registration when present.
- Phase7Composer constructor: new optional IAlarmHistorianWriter?
injectedWriter parameter (default null). Backward-compatible —
existing callers don't need to change; DI populates it
automatically when the singleton is registered.
- New static SelectAlarmHistorianWriter helper — resolution order
is driver → DI → null. Drivers win when both are present so a
future GalaxyDriver-as-IAlarmHistorianWriter takes the write
path directly, preserving the v1 invariant where a driver that
natively owns the historian client doesn't bounce through the
sidecar IPC.
- ResolveHistorianSink uses the helper + emits a structured log
line identifying which source provided the writer.
Tests:
- 4 SelectAlarmHistorianWriter precedence tests — no source / DI
only / driver wins over DI / first-driver-with-writer wins.
- Pre-existing 4 HostStatusPublisherTests SQL failures unrelated
to this change (require the docker-host SQL Server at
10.100.0.35,14330 per CLAUDE.md). Phase7 + alarm tests all
green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>