cfeb761092
rust / build / test / clippy / fmt (push) Has been cancelled
Closes F33. Final commit in the three-step F33 closure (218f4c4→7a5f251→ this) — propagates the F31 InvalidConnectionId tolerance pattern to every remaining response decoder + adds publish-loop detection so the F26 stream terminates cleanly on server-side rejections instead of spinning silently. Decoders updated to tolerate empty / missing payloads + surface result_code/success: - decode_publish_response (the F26 stream's hot path) - decode_unregister_items_response - decode_delete_monitored_items_response - decode_write_response - decode_publish_write_complete_response Shared `extract_result_status(body_tokens)` helper in operations.rs consolidates the per-decoder find_text_in_named_element calls for resultCodeField + successField — a single source of truth for the F31-pattern wrapper extraction. Public response structs gain `result_code: Option<u32>` and `success: Option<bool>`: - PublishResponse - UnregisterItemsResponse - DeleteMonitoredItemsResponse - WriteResponse - PublishWriteCompleteResponse asb_session.rs::publish_loop: when PublishResponse.result_code is Some(non_zero), the loop now sends Err(ConnectionError::TransportFailure { detail: "publish returned result_code 0xXX (server-side rejection)" }) as the stream's terminal item, then returns. Without this, an InvalidConnectionId-poisoned subscription would generate empty PublishResponse forever. 5 new tests synthesise the InvalidConnectionId wire shape (`<Result><resultCodeField>1</><successField>false</></><ASBIData/><ASBIData/>`) for each decoder via the shared synthesise_invalid_connection_id_body helper — pin the tolerance for Publish, Unregister, Delete*, Write, and PublishWriteComplete. Updated obsolete write_response_missing_status_fails test to write_response_missing_status_returns_empty_with_no_result_code since the decoder no longer errors. Live read regression test: TestChildObject.TestInt = 99 returned end-to-end after all changes (cargo run -p mxaccess --example asb-subscribe). Workspace: mxaccess-asb 82 → 87 tests (+5). All other crates unchanged. Default-feature clippy clean. design/followups.md: F33 moved to Resolved with the full three-commit audit trail. M5 status block stable: F32 + F33 closed, only F28 (canonical XML for the remaining 8 ops) remains as P2 latent — works in practice under empty hashAlgorithm. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mxaccess (Rust port)
Native Rust replacement for AVEVA / Wonderware MXAccess. See ../design/ for
the architectural specification, ../src/ for the .NET reference (the
executable spec), and ../CLAUDE.md for project-wide rules.
Status
M0 — Workspace skeleton. Stub types compile; nothing is implemented yet.
See ../design/60-roadmap.md for the M0–M6 milestone plan.
Layout
rust/
Cargo.toml workspace root
rust-toolchain.toml 1.85 stable
crates/
mxaccess-codec/ pure protocol codec, no I/O
mxaccess-galaxy/ Galaxy SQL resolver (tiberius)
mxaccess-rpc/ DCE/RPC + NTLMv2 + OXID + OBJREF
mxaccess-callback/ INmxSvcCallback RPC server
mxaccess-nmx/ INmxService2 client
mxaccess-asb-nettcp/ net.tcp framing (MC-NMF + MC-NBFX/NBFS)
mxaccess-asb/ IASBIDataV2 client
mxaccess/ async session + Transport trait + public API
mxaccess-compat/ LMXProxyServer-shaped facade
Build
cargo build --workspace
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo fmt --check
Live probes
. ..\tools\Setup-LiveProbeEnv.ps1
cargo test -p mxaccess --features live -- --ignored
The setup script fetches credentials from Infisical via
wwtools/secrets/Get-Secret.ps1. Never inline plaintext credentials.
License
MIT — see ../LICENSE.