D2 (new path): SDK-direct WCF revision orchestrator + probe

Implemented HistorianWcfRevisionOrchestrator that talks WCF directly
to /Trx, bypassing the native wrapper entirely. Probes
AddNonStreamValuesBegin2 against the live local Historian and surfaces
what the server returns. Internal-only API; no public surface added —
the path isn't viable yet.

Findings (live test against localhost):

-  The wire path is reachable. After moving from V1 (uint handle, no
  errorBuffer) to V2 (string handle GUID, out errorBuffer), the server
  recognizes the call (no ContractFilter mismatch, no exception).
-  Server processes the call and returns a structured 5-byte error
  buffer: 04 33 00 00 00 = type 4 (CustomError) + code 51
  (UnknownClient).
-  Tried four handle formats (contextKey upper/lower, storageSessionId
  upper, ClientHandle as decimal string) — all return the same
  UnknownClient.
-  Adding the full priming chain (Stat.GetV ×2, Stat.GETHI ×2, UpdC3,
  6× Stat.GetSystemParameter, AllowRenameTags, Trx.GetV, Stat.GetV,
  Retr.GetV) — same result.

ITransactionServiceContract2 has no Validate/Register/Open op of its
own. The client-with-Trx registration must happen via some cross-
service side effect we haven't isolated.

Important takeaway: the wire-format mismatch is solved (contract method
names + parameter shapes match what the server expects). The remaining
gap is a single missing initialization step. Documented in
docs/plans/revision-write-path.md as concrete next-session steps.

178/178 tests pass (one new probe test added). Probe is gated on
HISTORIAN_HOST=localhost.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 02:51:26 -04:00
parent b5e5f5485b
commit b40e6948e2
4 changed files with 375 additions and 0 deletions
+45
View File
@@ -134,6 +134,51 @@ path is implementable. If it fails with a server-side cache error,
try `RTag2` first. If it still fails, the path is genuinely blocked
server-side.
### SDK-direct probe results (2026-05-05)
`HistorianWcfRevisionOrchestrator` wires up the priming chain + a probe
of `ITransactionServiceContract2.AddNonStreamValuesBegin2(string handle, out string transactionId, out byte[] errorBuffer)`.
Live test against `localhost`:
-`OpenSucceeded: True` — Hist auth chain + Open2 still work end-to-end
- ✅ Trx channel opens, `Trx.GetV` returns interface version 2
- ✅ Wire path is recognized — server processes the call (no
`ActionNotSupportedException` after switching from the abbreviated
`AddNonS2B` to the default action name)
- ❌ Server returns structured error `04 33 00 00 00` =
type 4 (CustomError) + code 51 (`UnknownClient`) for all four handle
formats tried (contextKey GUID upper, storageSessionId upper, contextKey
lower, ClientHandle as string)
- ❌ Adding the full priming chain (Stat.GetV ×2, Stat.GETHI ×2, UpdC3,
6× Stat.GetSystemParameter, AllowRenameTags, Trx.GetV, Stat.GetV,
Retr.GetV) doesn't change the result — Trx still rejects with
`UnknownClient`
`ITransactionServiceContract2` exposes only `GetV`, `ForwardSnapshot*`,
and `AddNonStreamValues*`. There is no `ValidateClient`, `RegisterClient`,
or `Open` on Trx. So the client-with-Trx registration must happen via
some cross-service side effect we haven't identified.
**Important takeaway:** the wire path works at the WCF protocol layer.
We're past the "is this even reachable" question. The remaining gap is
finding what populates Trx's session table — likely:
1. `RTag2` on /Hist with a tag whose registration cascades to Trx
2. Some `IStorageServiceContract` op that we haven't tried
3. An aspect of the C++ HistorianClient initialization that doesn't
show up in the IL we've inspected (e.g., the
`aahClientCommon.CClientCommon` calls during InitializeProxy)
A future session that wants to push further should:
1. Add `RTag2` for the sandbox tag and retry Begin2 — quick experiment
2. If that fails, try sending the IStorageServiceContract.AddT or
similar to "introduce" the tag to Trx
3. If that fails, do an IL walk of `aahClientCommon.CClientCommon`
methods called between Open2 and AddNonStreamValuesBegin in a
working native scenario (using a system tag the wrapper would
accept — or capturing actual on-wire bytes via the IL-rewrite
instrumentation if possible)
## Decision
Do **not** add public `WriteRevisionsAsync` / `BeginRevisionAsync` to