Files
mxaccess/design
Joseph Doherty af15fe7587 [F49 step 1 + F56] callback router: peel envelope before parsing subscription / 0x11 frames
The router used to call NmxSubscriptionMessage::parse_inner directly
on the COM-stub-delivered body, but the wire bytes arrive wrapped in
a ProcessDataReceived envelope (46-byte header + optional 4-byte
length prefix); parse_inner expects post-envelope bytes. Result:
every 0x33 DataUpdate that ever arrived was silently dropped.

Mirrors the .NET reference's MxNativeSession.OnCallbackReceived flow
at cs:582-606 — three sequential parse attempts:
  1. NmxOperationStatusMessage::try_parse_process_data_received_body  (already wired)
  2. NmxReferenceRegistrationResultMessage::try_parse_...              (NEW — was missing)
  3. NmxSubscriptionMessage::try_parse_process_data_received_body      (NEW — was wrong)

Adds:
- NmxSubscriptionMessage::try_parse_process_data_received_body — peels
  envelope via NmxObservedEnvelope::parse_process_data_received_body_flexible,
  then dispatches to existing parse_inner.
- NmxReferenceRegistrationResultMessage::try_parse_process_data_received_body —
  same shape, for the 0x11 registration-result frame.
- Router branch for 0x11 — currently traces the assigned item_handle and
  drops the frame (matches the .NET reference, which fires a
  ReferenceRegistrationReceived event with no consumer in the codebase).
- Router fall-through trace! when neither path matches, so future
  unparseable bodies surface in RUST_LOG=trace instead of vanishing.
- DcomCallbackSink::forward — trace! per inbound callback so
  RUST_LOG=mxaccess_callback=trace surfaces opnum + size.
- crates/mxaccess-compat/tests/buffered_subscribe_live.rs — F49 step 1
  live test that drives subscribe_buffered + a 500ms-cadence writer.
  Also pulls tracing-subscriber as a dev-dep so the test can dump
  router activity.

Existing router_task_decodes_callback_invoked_into_broadcast unit test
updated to wrap its synthetic 0x32 body in an envelope so the new
parse path actually accepts it.

Live result: F56 — the buffered round-trip *registers* successfully
(RegisterReference returns HRESULT 0; engine sends one 0x11
RegistrationResult + one 51-byte op-status per write, perfectly
clocked) but the engine never sends a 0x33 DataUpdate. Rust-port-
specific gap vs the .NET reference's working buffered path; root
cause is likely a field-level difference in the RegisterReference
body or a missing post-RegisterReference step. Captured as F56 in
design/followups.md, blocking F49 step 1; F56's DoD is the same
live test reporting >=3 DataChange arrivals.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 09:50:57 -04:00
..

design/ — Rust port architectural plan

This folder is the design contract for the Rust replacement of AVEVA/Wonderware MXAccess. It is the gap between the .NET reference in src/ and the Rust crates that will be written under a sibling rust/ workspace (per CLAUDE.md).

The folder is structured as a small set of focused documents. Read in order; each builds on the previous.

File Purpose
00-overview.md Mission, two-layer goal, architectural principles, non-goals
10-raw-layer.md Byte-accurate raw MXAccess layer (codec + transport + session)
20-async-layer.md Idiomatic Tokio async layer on top of the raw layer
30-crate-topology.md Cargo workspace, crates, dependencies, build/test commands
40-protocol-invariants.md Bill of materials: IIDs, opnums, envelope/handle bytes
50-error-model.md MxStatus, error types, panic/cancellation policy
60-roadmap.md Milestones M0..M6, validation strategy
70-risks-and-open-questions.md Parity gaps, unproven flows, cross-platform constraints
dependencies.md Cross- and within-milestone parallelism map; agent budget per phase
review.md Adversarial review log (BLOCKER/MAJOR/MINOR/NIT findings, all resolved)
prompt.md /loop driver prompt for autonomous M2M6 execution
followups.md Open / resolved deferred work items; auto-triaged by prompt.md Step 0 (created on first /loop run if missing)

The design is grounded in the .NET reference at src/ and the protocol artifacts in docs/, analysis/, and captures/. Do not introduce protocol behavior in these documents that is not already proven in the reference. When adding a new claim about wire format, cite either:

  • a .cs file path in src/MxNativeCodec/, src/MxNativeClient/, or src/MxAsbClient/, or
  • a docs/*.md spec file, or
  • a captures/0NN-frida-* directory or analysis/frida/*.tsv row.

This folder is documentation, not code. When the Rust workspace is created, the design here is the contract it must satisfy. When evidence in captures/ invalidates a design decision here, update the design first, then the code.

Reading order

  • New contributor: 00 → 30 → 10 → 40 → 20 → 50 → 60 → 70.
  • Protocol question: 40 first, then the relevant section of 10.
  • API question: 20 first, then 50.
  • Planning a milestone: 60 first, cross-reference 70 for blockers.
  • Scheduling concurrent work: dependencies.md for the per-phase parallelism map.
  • Driving M2M6 autonomously via /loop: prompt.md (and the followups.md triage log it maintains).