fix(mtconnect): Unsupported latch inside the checked section; live-index reads do not clear a write degradation (review r1 N-4, N-5)

- N-4: the /sample Unsupported latch is set inside TryDegradeStream's
  generation-checked critical section (latchUnsupported), so a superseded pump can
  no longer latch it on the session that replaced it.
- N-5: a read served from the live pump index clears only the read degradation -
  it is stream evidence, like a chunk, which by the I-3 ruling does not clear an
  unanswered write. The 'cannot stick' wording in the XML doc and
  docs/drivers/MTConnect.md now states the latch-until-next-answered-/current-read-
  or-write behaviour, incl. the subscription-only consequence. Tested (chunk +
  live-index read keep Degraded; an answered write clears it).

Refs #743

Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 16:58:37 -04:00
parent c39bcd6e24
commit bc1f341e8a
3 changed files with 57 additions and 14 deletions
+5 -3
View File
@@ -437,9 +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 cleared by the next answered **write** or the next good **read** (a `/current`
answer or a live-index read), so it cannot stick on a driver still talking to the Agent; a
`/sample` chunk alone does not clear it.
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`.
**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