Materialize each native alarm ONCE at the raw tag (ConditionId = RawPath, Raw
realm); wire the single condition as an SDK event notifier of each referencing
equipment's UNS folder so one ReportEvent fans to every root without
re-reporting per root (which would break Server-object dedup + Part 9 ack
correlation).
- New sink method WireAlarmNotifiers(alarmNodeId, alarmRealm,
notifierFolderNodeIds, notifierFolderRealm) on IOpcUaAddressSpaceSink,
forwarded through DeferredAddressSpaceSink + SdkAddressSpaceSink +
NullOpcUaAddressSpaceSink (the forwarding-trap guard); auto-covered by the
DeferredSinkForwardingReflectionTests realm + forwarding guards + a
hand-written forward test.
- OtOpcUaNodeManager: the normative AddNotifier(isInverse) pair + idempotent
EnsureFolderIsEventNotifier per equipment folder; tracked per condition in
_alarmNotifierWiring. Teardown symmetry: RemoveNotifier(bidirectional:true) on
RebuildAddressSpace, RemoveAlarmConditionNode, and RemoveEquipmentSubtree so no
inverse-notifier entry leaks across redeploys.
- AddressSpaceApplier.MaterialiseRawSubtree wires notifiers for each native alarm
tag, resolving its ReferencingEquipmentPaths (Area/Line/Equipment) to the
EquipmentId folder NodeIds via BuildEquipmentIdByFolderPath.
- AlarmTransitionEvent gains ReferencingEquipmentPaths (empty default); /alerts
renders the referencing-equipment list as display metadata.
- Un-skipped + rewrote the native-alarm dark tests
(DriverHostActorNativeAlarmTests x6, DriverHostActorNativeAlarmAckRoutingTests
x1) for the v3 raw-condition model; new NodeManagerMultiNotifierAlarmTests
proves multi-notifier wiring + teardown symmetry (no leaked duplicates after a
re-trip) + applier wiring test.
Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
Fix TESTS only (production already green) so the four in-scope test projects
compile + pass under the v3 dark address space:
- Core.Abstractions.Tests: drop AllowedNamespaceKinds/NamespaceKindCompatibility
from DriverTypeMetadata; rewrite EquipmentTagRefResolver tests to the single-func
RawPath-lookup contract (no parseRef/transient cache).
- Core.Tests: rewrite EquipmentNodeWalkerTests to EquipmentNamespaceContent(Areas,
Lines,Equipment,VirtualTags?,ScriptedAlarms?) — folders + VirtualTags +
ScriptedAlarms only; raw-tag variables dark (skipped Batch-4 placeholder). Drop
retired Equipment.DriverInstanceId.
- Runtime.Tests: rewrite golden corpus + TagConfigCorpusParityTests to the v3
RawPath/RawTagEntry round-trip (byte + TagConfigIntent parity; EquipmentTags dark).
Salvage VirtualTag + ScriptedAlarm artifact parity to the new Compose signature.
Retire the equipment-tag-materialization parity files (Array/Historize/Alias) and
the equipment-device-binding DeviceHost parity to documented skipped placeholders.
Skip 34 dark equipment-tag routing/value/alarm/write/discovery actor tests with a
shared DarkAddressSpaceReasons constant. Re-key cluster-scoped tests to v3 UNS
line->area attribution.
- ControlPlane.Tests: ConfigComposer round-trips re-keyed to RawFolder/Device (no
Namespaces); tag-config gate resolves driver via Device; deploy-gate collision test
re-keyed to UnsEffectiveNameCollision; drop retired BadCrossClusterNamespaceBinding
case.