docs(#456): live grind finding — write-trigger structurally impossible
v2-ci / build (pull_request) Successful in 4m29s
v2-ci / unit-tests (pull_request) Failing after 10m15s

Ran the full item-1 grind (authored a live Modbus equipment tag, connected
to the sim, LDAP-authed writes, docker pause the peer). 4 failing wrapped
writes produced 0 retry/breaker lines on either central.

Root cause (corrects the issue's hypothesis): ModbusDriver.WriteAsync swallows
all exceptions and returns WriteResult(StatusBadInternalError) — never throws;
CapabilityInvoker feeds only exceptions to Polly; breaker ShouldHandle is
Handle<Exception>; Write retry pinned to 0 (R2-02/S-8). So a failing wrapped
write emits no line by construction, for any polling driver. The line is
reachable ONLY for a session driver (OpcUaClient) faulting mid-Discover/Subscribe
(30s Polly timeout throws) — a production timing race, not deterministically
forcible on the rig. Behaviour stays unit-proven by the pipeline-builder test.

- New: archreview/plans/artifacts/456-retry-breaker-live-finding-2026-07-15.md
- FOLLOWUP-10 updated with the structural finding + recommendation to close item 1.
This commit is contained in:
Joseph Doherty
2026-07-15 14:48:03 -04:00
parent c417502db1
commit 5da7ba6517
2 changed files with 105 additions and 0 deletions
@@ -101,6 +101,28 @@ Original recipe (retained): build central-1/central-2 from source → deploy a M
grep central-1 logs for `Driver resilience retry` / `circuit-breaker OPENED` / `closed`. NB the Modbus-path caveat
above — an idempotent write (or an S7 driver) is the reliable trigger, not a plain subscribe/read.
## UPDATE (2026-07-15, Gitea #456) — the write-trigger is STRUCTURALLY IMPOSSIBLE; line is OpcUaClient-only
Ran the full grind (authored a live Modbus equipment tag, connected to the sim, drove LDAP-authed writes, then
`docker pause`d the peer). **4 failing wrapped writes → 0 retry/breaker lines** on either central.
[Finding artifact](artifacts/456-retry-breaker-live-finding-2026-07-15.md). Root cause corrects the recipe's
"idempotent write is the reliable trigger" note:
- **`ModbusDriver.WriteAsync` swallows every exception → returns `WriteResult(StatusBadInternalError)` — it
never throws** (`ModbusDriver.cs:964-967`); the `CapabilityInvoker` only feeds *exceptions* to Polly
(`CapabilityInvoker.cs:84/112/147`, no result-status inspection); breaker `ShouldHandle` is `Handle<Exception>`
and `Write` retry is pinned to 0 (R2-02/S-8). So a failing wrapped **write** emits **no line by construction**,
for **any** polling driver (they all map write faults to a status). Even an idempotent write to a dead endpoint
returns a bad status, not a throw.
- Polling drivers' other wrapped sites don't throw through the seam either (`SubscribeAsync` lazy poll-reg;
`DiscoverAsync` offline/config-driven).
- **The line is reachable ONLY for a session driver (OpcUaClient)** whose session faults *mid-`Discover`/
`Subscribe`* (the 30 s Polly timeout on the post-connect discovery loop throws `TimeoutRejectedException`
`Discover` retry ×2). That is a genuine production event but an inherent **timing race** (connect must succeed,
the fault must land during the wrapped call before keepalive drops the driver to `Reconnecting`) — not
deterministically forcible on the rig. The behaviour stays **unit-proven** by the pipeline-builder logging test.
**Recommend closing item 1 on this finding.**
---
## (Original filed analysis — retained for history; superseded by OUTCOME above)