First live-verified gRPC read against a real 2023 R2 Historian. The handshake
previously failed at round 0 (cred-independent) because the SSPI/Negotiate token
loop was routed to HistoryService.ExchangeKey. ExchangeKey is a separate
key-exchange/cert-path op, not the Negotiate loop — the token loop belongs on
StorageService.ValidateClientCredential, which kept the 2020 inBuff/outBuff token
framing the SDK's WrapValidateClientCredentialToken/TryRead helpers already build.
Captured + diffed against the recovered 2023 R2 protobuf contract and the
decompiled stock client; routing the loop to ValidateClientCredential completes
the full chain (ValidateClientCredential x N -> OpenConnection -> StartQuery ->
GetNextQueryResultBuffer) and returns rows.
- HistorianGrpcReadOrchestrator: token loop now calls
StorageService.ValidateClientCredential(Handle, InBuff); corrected the op-map
doc comment (was asserting the wrong ExchangeKey mapping).
- HistorianServerVersionGate: accept History interface version 12 alongside 11.
Live server reports History=12, Retrieval=4, Storage=4; the buffers are
byte-identical (a live read returns rows), so 12 is buffer-compatible. Retrieval
stays pinned at 4 (matches). New AcceptedVersions() supports multi-version gates.
- New HistorianGrpcHandshakeRoutingTests: IL-level structural guardrail that
disassembles the orchestrator (incl. lambda closures) and asserts the handshake
invokes ValidateClientCredential and never ExchangeKey — fails if the regression
returns.
- Updated gate tests + CLAUDE.md gRPC op-map.
240 unit tests pass on the full stack; 210 on this branch's base. The byte
payloads remain the proven 2020 protocol.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC