fix(mtconnect): a delivered /sample chunk clears an unanswered-write degradation (review r2 N-5)

Coordinator ruling: a delivered chunk proves the Agent is alive (the analogue of
OpcUaClient's good keep-alive), so TryMarkStreamDelivered - generation-checked
under _subscriptionLock - also clears _writeDegraded. The degradation no longer
sticks forever on a subscription-only driver. Test: unanswered write -> Degraded,
stays Degraded with nothing else happening (positive control), the next chunk ->
Healthy; red with the clear removed. Round-2 doc text corrected.

Refs #743

Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 17:08:44 -04:00
parent bc1f341e8a
commit 419ecb3ce9
3 changed files with 26 additions and 21 deletions
+5 -5
View File
@@ -437,11 +437,11 @@ refused — advances `LastSuccessfulRead`, with the state following the read / `
(a write cannot clear either of those paths' degradation). A write that got **no** answer
(transport failure, deadline) sets **Degraded** with the stamp preserved and a type-only
`LastError` (review 2026-10-21 I-3, coordinator ruling). One success writer is shared: that
degradation is **latched until the next answered write or the next answered `/current` read**.
Neither a `/sample` chunk nor a read served from the live pump index clears it (both are stream
evidence, not an answer to a request), so on a **subscription-only** driver one timed-out write
keeps the driver Degraded until the next write — the comms-loss watchdog does not trip on it,
because the stream keeps advancing `LastSuccessfulRead`.
degradation is cleared by the next answered **write**, the next answered **`/current` read**, or
the next **`/sample` chunk** the current session's pump delivers (review r2 N-5: a delivered
chunk proves the Agent is alive — the analogue of OpcUaClient's good keep-alive — so it cannot
stick on a subscription-only driver). A read served from the live pump index does not clear it
by itself; the chunks behind that index do.
**One request per device (review 2026-10-21 T-6).** The slots that survive the refusals above
go out as one `POST {agent}/{device}?id1=v1&id2=v2…` per device (the Agent's PUT surface takes