HandleRestartDriver stopped + respawned the child within one synchronous
message handler, reusing the base actor name drv-<id>. Context.Stop is async
(the child processes its own stop on its own mailbox), so the old child was
ALWAYS still registered when the respawn ran — Context.ActorOf threw
InvalidActorNameException deterministically on every AdminUI Restart press,
crashing + restarting the host.
Fix: a monotonic _childSpawnGeneration counter (single-threaded actor) feeds a
-g<gen> suffix on every spawned child name, so a respawn can never collide with
the still-terminating predecessor. Children are tracked by the _children dict
(by IActorRef), never by actor path, so the suffix is invisible to callers.
This also closes the same-shaped latent race in the reconcile path (a removed-
then-readded instance, and a driver-type-change ToStop+ToSpawn in one plan).
Regression test RestartDriver_respawns_the_child_without_an_actor_name_collision
(verified: FAILS on the old code with the exact InvalidActorNameException,
PASSES with the fix). Runtime.Tests 238/238 green. Code-reviewed (approved).
Phase B native alarms never fired end-to-end: GalaxyDriver suppresses OnAlarmEvent until
an alarm subscription exists (_alarmSubscriptions.Count > 0), but the runtime only attached
the OnAlarmEvent handler and never called SubscribeAlarmsAsync — so the central feed stayed
gated and no transition reached the Part 9 condition / /alerts. Unit tests passed because
they inject through the IAlarmSource seam directly; the deferred live /run surfaced it.
DriverHostActor computes per-driver alarm refs (alarm-bearing tags' FullNames) and hands them
via SetDesiredSubscriptions; DriverInstanceActor calls SubscribeAlarmsAsync for IAlarmSource
drivers on Connected entry and whenever alarm refs are pushed while Connected (the deploy path),
idempotent via a cached handle reset on detach so reconnect re-subscribes.
Add AllDeadLetters probe to Native_alarm_during_reconnect_is_dropped_not_forwarded so the
test genuinely guards the Reconnecting state's Receive<NativeAlarmRaised> drop handler —
removing that handler would now cause a dead-letter and fail the assertion (false-negative
gap closed). Reword the ScriptedAlarms.md severity-mapping note: "snaps on the first
transition" → "every transition maps … overriding the authored seed from the first
transition onward", clarifying that MapSeverity runs on every event, not just the first.
Stop parsing TagConfig twice per tag on the deploy hot path: Phase7Composer's
equipment-tag Select lambda is now block-bodied (captures isHistorized/historianTagname
once), and DeploymentArtifact.BuildEquipmentTagPlans captures locals before result.Add.
Add wrong-type-historianTagname InlineData to ExtractTagHistorizeTests. Extend the
parity round-trip fixture with a 4th tag (isHistorized:false + JSON-null tagname)
exercising the artifact-side private guard path. Align DeploymentArtifact's
ExtractTagHistorize doc-comment with the composer-side phrasing (ExtractTagFullName /
ExtractTagAlarm cross-reference).
A DriverInstanceActor stuck Reconnecting/Connecting now adopts a config delivered via ApplyDelta and
re-initialises with it, instead of dead-lettering and retrying the stale config forever. A monotonic
init generation supersedes the in-flight init so the corrected config always wins.