# Followups Open work items deferred during /loop iterations. Triaged at the top of every iteration. New items are appended under `## Open`; resolved items move to `## Resolved` with a date + commit hash. ## Open ### F1 — NTLM consumer-layer helpers (workstation default + from_env constructor) **Severity:** P3 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/ntlm.rs` **Why deferred:** The .NET reference's `Environment.MachineName` default for `workstation` and its `FromEnvironment()` constructor (`ManagedNtlmClientContext.cs:38`, `:41-49`) read host state and env vars — both side effects that don't belong in a pure codec module. The constructor takes `workstation: Option<&str>` so callers can wire either later. **Resolves when:** M2 wave 2 transport (or the M2 example `connect-nmx.rs`) wires `NtlmClientContext::new(.., Some(hostname()?))` and provides a small `from_env` helper at the consumer layer. ### F2 — NTLM verify_signature path + constant-time MAC compare (server-to-client direction) **Severity:** P2 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/ntlm.rs` **Why deferred:** The .NET `ManagedNtlmClientContext` only implements client-to-server signing (`cs:30,124`); there is no implementation of server-to-client sign/seal keys or `verify_signature`. Both are needed when the callback exporter receives a signed inbound frame from `NmxSvc.exe`, but no such fixture exists yet. **Resolves when:** M2 wave 3 (callback exporter) captures an `INmxSvcCallback::StatusReceived` frame with an `auth_value` trailer per `design/60-roadmap.md:56` (DoD #3) and a fixture lands under `tests/fixtures/m2-status-frame/`. Add `subtle = "2"` and gate the byte compare behind `ConstantTimeEq` at the same time. ### F3 — Cross-domain NTLM Type1/2/3 fixture **Severity:** P2 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/ntlm.rs` **Why deferred:** All current NTLM fixtures are single-domain (the local AVEVA install). Tracked separately in `design/70-risks-and-open-questions.md` R8 (P1 risk) and the open-evidence-gaps table. **Resolves when:** A multi-domain AVEVA test harness lands and a successful cross-domain authenticate round-trip captures Type1/2/3 bytes. Notes: this clears R8. ### F4 — BindAck / AlterContextResponse body parser **Severity:** P2 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/pdu.rs` **Why deferred:** The .NET reference (`DceRpcPdu.cs:217-262`) parses Bind and AlterContext into the same struct but does not decode the corresponding *response* body (result list + secondary address). The Rust port's `BindPdu::decode` accepts `BindAck` packet type but does not interpret the body. The negotiated transfer syntax — needed before opnum dispatch — is currently inferred from request-side context. **Resolves when:** A captured BindAck frame from `captures/013-loopback-subscribe-scalars/nmx-stream-*.bin` is decoded and the body shape is documented in `docs/Loopback-Protocol-Findings.md`. ### F5 — Captured DCE/RPC bind-frame fixture round-trip **Severity:** P2 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/pdu.rs` **Why deferred:** Existing PDU tests build hand-constructed `[C706]`-conformant frames. A capture-driven round-trip (extract bind/alter PDUs from `captures/013-loopback-subscribe-scalars/nmx-stream-*.bin`, decode → encode → assert byte-identical) would be stronger evidence of parity with the live wire. **Resolves when:** Bytes from that capture are extracted into `tests/fixtures/m2-pdu/` and the round-trip test lands. ### F6 — Port `ComObjRefProvider.cs` (OBJREF emitter via Win32 CoMarshalInterface) **Severity:** P2 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/objref.rs` **Why deferred:** The provider is a wrapper around `ole32::CoMarshalInterface` / `IStream` / `GlobalLock` / `GlobalSize`. It needs `windows-rs`, which is currently behind the `windows-com` feature in `mxaccess-rpc/Cargo.toml`. The pure-Rust parser stands alone for the inbound activation-response path that M2 wave 1 needs. **Resolves when:** `windows-rs` is wired into `mxaccess-rpc` (M2 wave 3 callback exporter needs to publish its own OBJREF for `IRemUnknown` / `INmxSvcCallback` registration) and an emitter port lands behind the `windows-com` feature. ### F7 — Consolidate `Guid` type across `mxaccess-rpc` **Severity:** P3 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/{objref.rs,pdu.rs}` **Why deferred:** `objref::Guid` is a self-contained `[u8; 16]` newtype with `Display` matching `.NET Guid.ToString("D")`. `pdu::SyntaxId` uses raw `[u8; 16]` for IIDs. Both work but a single shared type would be cleaner. **Resolves when:** A small consolidation lands — either `pub use objref::Guid as Guid;` from `pdu`, or both move to a shared `crate::guid` module. Trivial; pick during M2 wave 2 when the next agent touches the crate. ### F8 — `RpcError` is duplicated across `objref` and `pdu` modules **Severity:** P3 **Source:** M2 wave 1, `crates/mxaccess-rpc/src/{objref.rs,pdu.rs}` **Why deferred:** Each module defined its own `RpcError` enum with a partial set of variants. Both are sound in isolation but the crate-public `RpcError` should be a single union. Not blocking — they don't collide because each module re-exports its own. **Resolves when:** M2 wave 2 (OXID + `IRemUnknown::RemQueryInterface`) needs a third error surface. At that point, hoist `RpcError` to `crates/mxaccess-rpc/src/error.rs` mirroring `mxaccess-codec/src/error.rs`, and have each module use the shared enum. ## Resolved (none yet)