[F34 evidence] dump WCF binary-header dictionary for AddMonitoredItems
rust / build / test / clippy / fmt (push) Has been cancelled

Extends tests/add_monitored_items_request_capture.rs with a manual
binary-header walk that prints every pre-interned string + its wire
id. The captured request's binary header pre-declares **23 strings**
covering the entire DataContract field set:

  wire-id  1  http://ASB.IDataV2:addMonitoredItemsIn
  wire-id  3  AddMonitoredItemsRequest
  wire-id  5  SubscriptionId
  wire-id  7  Items
  wire-id  9  http://schemas.datacontract.org/.../ASBIDataV2Contract
  wire-id 11  MonitoredItem
  wire-id 13  activeField
  wire-id 15  activeFieldSpecified
  wire-id 17  bufferedField
  wire-id 19  itemField
  wire-id 21  contextNameField
  wire-id 23  idField
  wire-id 25  idFieldSpecified
  wire-id 27  nameField
  wire-id 29  referenceTypeField
  wire-id 31  typeField
  wire-id 33  sampleIntervalField
  wire-id 35  timeDeadbandField
  wire-id 37  timeDeadbandFieldSpecified
  wire-id 39  userDataField
  wire-id 41  lengthField
  wire-id 43  payloadField
  wire-id 45  valueDeadbandField

That gives F34's binary-builder rewrite the exact dict-id mapping
to target — every MonitoredItem child can be emitted as a
DictionaryStatic(odd-id) reference instead of an inline string,
matching WCF's compression. The "RequireId" mystery from the
earlier inline-name decode is also resolved: the wire body has
NO `RequireId` element at the bottom — the trailing `Inline("referenceTypeField")` was a dict-id wraparound or auto-intern artifact, not actual content.

design/followups.md F34 updated with the full ground-truth header,
plus a refined "Resolves when" pointing at the underlying
`nbfx.rs::decode_tokens` auto-intern semantics. The current codec's
doc comment ("the codec doesn't auto-intern") is correct for raw
[MC-NBFX] but wrong for WCF binary messages where the writer
auto-interns by convention; that's the structural fix the F34 binary
rewrite depends on.

No code-path change in this commit beyond the test improvements.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-06 03:05:20 -04:00
parent b66f5bb018
commit d03bd04ef5
2 changed files with 54 additions and 1 deletions
@@ -38,6 +38,29 @@ fn add_monitored_items_request_capture_decoder_trace() {
let envelope = &raw[3..];
assert_eq!(envelope.len(), 692);
// Manually walk the leading WCF binary header (length-prefixed
// string list) so we can dump every interned string + its wire
// id. Mirrors what `decode_envelope::parse_binary_header_prefix`
// does internally; reproducing it inline so the test sees the
// raw strings.
use mxaccess_asb_nettcp::nmf::decode_multibyte_int31;
let mut cursor = 0usize;
let outer_len = decode_multibyte_int31(envelope, &mut cursor).expect("outer-len varint");
eprintln!("=== binary-header outer length: {outer_len} ===");
let header_start = cursor;
let header_end = header_start + outer_len as usize;
let mut p = header_start;
let mut idx = 0usize;
while p < header_end {
let len = decode_multibyte_int31(envelope, &mut p).expect("string-len varint");
let bytes = &envelope[p..p + len as usize];
let s = std::str::from_utf8(bytes).expect("utf-8 header string");
let wire_id = (idx as u32) * 2 + 1;
eprintln!(" header[{idx}] (wire-id {wire_id}) = {s:?}");
p += len as usize;
idx += 1;
}
let mut dict = DynamicDictionary::new();
let decoded = decode_envelope(envelope, &mut dict).expect("decode_envelope succeeds");
eprintln!("=== body tokens ({} total) ===", decoded.body_tokens.len());