test(runtime): operator restart re-subscribes the respawned child (archreview 03/S16, U6)

Seen RED against a T3-reverted build: all 4 failed, Created[1].SubscribeCount == 0
on the headline case. 20x isolation run green after the fix was restored.
This commit is contained in:
Joseph Doherty
2026-08-20 09:53:38 -04:00
parent b3a71f6655
commit d97e441979
4 changed files with 360 additions and 7 deletions
@@ -537,3 +537,61 @@ added: `IReadOnlyDictionary<string, string>? tagConfigByTag = null`. The seeder
alarm-bearing raw tag that `Operator_restart_re_subscribes_alarm_refs_too` (a test the plan itself
specifies) requires. It is trailing and optional, so it is the same source-compatible shape as
`enabled`.
### T5 — `DriverHostActorRestartTests` (operator-restart falsification, 03/U6's first half)
**RED observed against a T3-reverted build** (the `SendDesiredSubscriptions(child, …)` call in
`SpawnChild` commented out, everything else on the branch): **all 4 FAILED, 0 passed**, each at the
bar the plan predicts —
```
Operator_restart_re_subscribes_the_respawned_child [FAIL]
factory.Created[1].SubscribeCount should be greater than or equal to 1 but was 0
Operator_restart_re_subscribes_alarm_refs_too [FAIL]
factory.Created[1].SubscribeAlarmsCount should be greater than or equal to 1 but was 0
Restart_of_a_driver_with_an_empty_value_set_still_sends_the_set [FAIL]
respawned.SubscribeAlarmsCount should be greater than or equal to 1 but was 0
A_child_spawned_by_a_fresh_deploy_ends_with_the_NEW_refs [FAIL]
factory.Created[2].LastSubscribedRefs should be ["Plant/Modbus/dev1/temp"] but was null
```
The call was then restored; **4/4 pass**, and the class was run **20× in isolation, 20/20 green** (#500
discipline for this assembly).
**Two deviations, both found by running the tests rather than by reading:**
1. ⚠️ **The plan's claim that the transient spawn-time set "is never acted on" is FALSE, and the
production comment that repeated it has been corrected.** The plan reasons that the child is still
`Connecting` when the stale set arrives, so it is only stored. But the child runs **concurrently**
with the rest of `HandleArtifactForApply` — `Context.ActorOf` returns and the child starts
processing while the host is still building `refsByDriver` — so a driver whose `InitializeAsync`
completes instantly reaches Connected and subscribes the stale set *before* the bulk push arrives.
Measured directly: `SubscribeCount == 2` on the respawned child, first with revision A's ref and
then with B's. What Akka's per-pair ordering does guarantee is **convergence**, and
`HandleSubscribeAsync` drops the prior subscription before establishing the new one, so the cost is
one extra subscribe round-trip on a *respawning* deploy — a real device driver, whose initialise
opens a socket, rarely wins that race. Accepted rather than "fixed": withholding the spawn-time send
is the 03/S16 defect, and the operator-Restart and supervision paths have no bulk push coming at
all. The `SpawnChild` comment now states this instead of the plan's claim, and the test asserts
convergence (the plan's own stated acceptance — "the child's live subscription carries the NEW
refs") rather than pinning the race.
2. **`A_child_spawned_by_a_fresh_deploy_ends_with_the_NEW_refs` had to be re-shaped.** The plan
describes revision B as one "which respawns nothing but re-pushes". It respawns: the raw tags are
folded into the merged `DriverConfig`, so a tag-set edit changes the `DriverInstanceSpec` and trips
the #516 config respawn. That is the *stronger* arrangement for this test's subject (a brand-new
child spawned while the authority still holds A), so the test keeps the same name and asserts the
respawned child converges on B — plus a subsequent operator Restart, which pins that the authority
is repopulated **whole** per apply rather than accumulated.
3. **`Restart_of_a_driver_with_no_authored_tags_sends_an_empty_set_not_nothing` was renamed
`Restart_of_a_driver_with_an_empty_value_set_still_sends_the_set`, with a real positive control.**
The seeder derives `DriverInstances` from the raw-tag rows, so a driver with *zero* tags is not
expressible, and — more importantly — an all-empty set has **no driver-side observable at all**
(the child stores it and, having no handle, does nothing), which would have made the test vacuous:
"it did not subscribe" passes equally against a build that sends the respawned child nothing, i.e.
against the defect itself. The subject is therefore a driver whose only raw tag is alarm-bearing:
its value half is genuinely empty while its alarm half is not, and **both halves ride in one
message** — so `SubscribeAlarmsCount >= 1` proves the message ARRIVED and `SubscribeCount == 0`
then proves the empty value half was honoured. The falsification run above confirms the control
works: with T3 reverted this test fails on the *positive control*, not on the absence.
**Not covered, and worth a reviewer's eye:** the `_desiredByDriver` union's `_children.Keys` arm
(a running child absent from BOTH ref maps) is not reachable through this seeder and is untested.
@@ -31,7 +31,7 @@
{
"id": "T5",
"subject": "DriverHostActorRestartTests: operator restart re-subscribes the respawned child (falsifiable; 03/S16, U6)",
"status": "pending",
"status": "done",
"blockedBy": [
"T3",
"T4"