[F33] design/followups: capture live-subscribe wire gap

Live run of `cargo run -p mxaccess --example asb-subscribe` against
the local AVEVA install (with DH params + passphrase loaded from
Setup-LiveProbeEnv.ps1 + Get-AsbPassphrase.ps1) surfaced two concrete
gaps in the subscription-path response decoders:

1. `CreateSubscriptionResponse` returns subscription_id = 0 — the
   server almost certainly assigns a real Int64, but
   decode_create_subscription_response can't locate the
   `<SubscriptionId>` element. Likely a dict-id our F30 post-pass
   doesn't resolve for that specific element name.

2. `AddMonitoredItemsResponse` decode fails with MissingField
   "Status". The wire shape needs a capture-and-diff vs the .NET
   probe's subscription path.

Once subscribe-side ops are issued, the channel desyncs — subsequent
read() on the same session fails with the same MissingField error,
suggesting NBFX framing state may also be out of sync.

The F26 stream API itself (AsbSession::subscribe → Stream<Item =
Result<MonitoredItemValue, Error>>) is complete and unit-tested
(commit f2f22df). This followup just captures the live-wire
reconciliation work that's still required to make the subscribe
path actually return data against MxDataProvider. Once F33 closes,
the last M5 live-wire gap is resolved.

P2 — not blocking M5 closeout; blocks the Subscribe demo.

The asb-subscribe.rs example stays in its working Read-loop form
(no regression). When F33 lands, the example can be promoted to
demonstrate the full subscribe flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-06 01:17:09 -04:00
parent f2f22dfcd1
commit cbc95a4684
+46
View File
@@ -146,6 +146,52 @@ move to `## Resolved` with a date + commit hash.
F25 (`mxaccess-asb` IASBIDataV2 client) and F26 (`mxaccess::Session` over `AsbTransport`) remain open. With F19-F24 landed, the M5 framing/encoder layer (streams A+B+C+D and the codec stream) is complete; F25 composes them into the `IASBIDataV2` wire client. F22's static dictionary subset is intentionally curated; expand entries as wire captures show new IDs. F27 (constant-time DH) is filed as a separate follow-up below.
### F33 — Live wire reconciliation for the ASB subscription path
**Severity:** P2 — not blocking M5 closeout (the F26 stream API ships with full unit-test coverage), but blocks the `examples/asb-subscribe.rs` Subscribe demo.
**Source:** Live run of `cargo run -p mxaccess --example asb-subscribe` against the local AVEVA install, 2026-05-06.
**Evidence captured live (with `MX_LIVE` + DH params + passphrase from `tools/Setup-LiveProbeEnv.ps1` + `tools/Get-AsbPassphrase.ps1`):**
```
connecting ASB at [fe80::3608:256c:365:cc73%6]:808 ...
connected; lifetime=Some("60000:V2")
registering TestChildObject.TestInt
register status: 0 item(s); first error_code = 0x0000
creating subscription (max_queue=100, sample=1s) ...
subscription_id = 0 ← suspicious
adding monitored items
add_monitored_items failed:
operation error: response is missing required field Status
```
Two distinct symptoms:
1. **`CreateSubscriptionResponse` returns `subscription_id = 0`** — the
live MxDataProvider almost certainly assigns a real Int64 ID, but
`decode_create_subscription_response` (operations.rs:861) walks
`body_tokens` looking for `<SubscriptionId>` and finds nothing
because the wire emits the element name as a dict-id rather than
inline UTF-8. Likely a dict-id we don't yet resolve in the F30
post-pass for this specific element.
2. **`AddMonitoredItemsResponse` decode fails with `MissingField "Status"`** —
`decode_add_monitored_items_response` expects a `<Status>` array
payload via `collect_asbidata_payloads`. The server's actual
response shape needs a capture-and-diff to compare against the
expected layout.
Once subscribe-side ops are issued, the channel ends up desynced —
even subsequent `read()` on the same session fails with the same
`MissingField "Status"` error, suggesting NBFX framing state may also
be out of sync after the failed subscription decode.
**Resolves when:** A side-by-side capture via `examples/asb-relay.rs`
(TCP middleman) running the .NET probe's subscription path reveals
the actual wire bytes for `CreateSubscriptionResponse` +
`AddMonitoredItemsResponse`. Reconcile the dict-id resolution and
response decoder accordingly. The subscribe path then closes the
last live-wire gap on M5 — the F26 stream API itself
(`AsbSession::subscribe`) is already complete and unit-tested.
### F28 — Canonical XML serialiser for `ConnectedRequest` signing (matches `XmlSerializer.Serialize` byte-for-byte)
**Status: PARTIALLY RESOLVED.** The five `[XmlSerializerFormat]` ops (AuthenticateMe, Disconnect, KeepAlive, RegisterItems, UnregisterItems) plus the per-action `ValidatorWireFormat` selector + DH-params-from-registry + dynamic-dict id management all landed in commits `f14580e` / `104efc4`. Live AuthenticateMe + RegisterItems work end-to-end (commit `9063f10`). Read / Write / CreateSubscription / AddMonitoredItems / Publish / DeleteMonitored / DeleteSubscription / PublishWriteComplete still sign over NBFX wire bytes via the legacy fallback; works in practice because the live registry has empty `hashAlgorithm` (no HMAC required for the unforced-MAC path), but will break under any deployment that sets a real algorithm. **Severity now P2** — promote back to P0 if a hashAlgorithm-non-empty environment is in scope.
**Severity:** P0 — blocks every signed ASB operation (AuthenticateMe, RegisterItems, all data-plane RPCs).