M3 R3.1: OpenStorageConnection is a dead end (error 85); precondition is front-door RegisterTags

Live-probed StorageService.OpenStorageConnection against the 2023 R2 server over a
write-enabled (0x401) session. Every attempt — sweeping ConnectionMode (0x401/0x402/0x1),
StorageSessionId-in (Open2-GUID / empty), and FreeDiskSpace — returns the IDENTICAL native
error type=4 code=85 ("session not registered"), so it's a structural refusal, not a bad
field value.

Decode (two corroborating facts):
- Error 85 is the same code the event read returns before RegisterTags2 (see
  HistorianWcfEventOrchestrator) — a generic "session not registered for this op".
- The 2023 R2 decompile shows OpenStorageConnection lives on a SEPARATE GrpcStorageClient
  (the storage engine's SF/snapshot channel, own port + service identity); HistorianAccess
  drives non-streamed writes through the native C++ HistorianClient, never this op.

So the roadmap's mapped "missing console session" step was wrong. The real non-streamed-write
precondition is the front-door HistoryService.RegisterTags (RTag2-family) for the target tag —
which is exactly why the R3.1 batch failed at AddNonStreamValues (no tag registered ->
StoreNonStreamValues had no route). Matches the original 2020-WCF D2 hypothesis.

Remaining (both need a native gRPC capture; do not guess bytes): the regular-tag RegisterTags
btTagInfos (only CM_EVENT's tag-GUID form is known) and the AddNonStreamValues btInput.

- HistorianGrpcStorageConnectionProbe + grpc-open-storage-connection CLI (opens nothing
  persistent; CloseStorageConnection on success)
- corrected revision-write-path.md §R3.1 follow-up + hcal-roadmap R3.1/R3.2 rows
- gated regression test pinning the error-85 refusal

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
Joseph Doherty
2026-06-21 18:51:16 -04:00
parent 78cb689bdf
commit 57b9506d01
5 changed files with 345 additions and 5 deletions
+3 -3
View File
@@ -254,8 +254,8 @@ byte-correct `AddS2` (✅). Appears-and-reads-back is environment-gated on event
| ID | Work | gRPC op | Status |
|---|---|---|---|
| R3.1 | Decode non-streamed VTQ packet | `Transaction.AddNonStreamValuesBegin/AddNonStreamValues/End` | 🟡 **gRPC Begin/End LIVE-VERIFIED 2026-06-21; full sequence MAPPED** (WCF still blocked — D2). Live decode showed `AddNonStreamValues` reaches server `StoreNonStreamValues``\\.\pipe\aahStorageEngine\console` and fails for lack of a console session. Remaining (follow-up): `StorageService.OpenStorageConnection` handshake + `RegisterTags`, THEN the `btInput` decode. See [`revision-write-path.md`](revision-write-path.md) §R3.1. |
| R3.2 | `AddHistoricalValuesAsync` | batched begin→values→end | 🟡 unblocked architecturally; needs R3.1's two live decode loops (OpenStorageConnection handshake + `btInput` serializer) then a real `bCommit=true` write/read-back |
| R3.1 | Decode non-streamed VTQ packet | `Transaction.AddNonStreamValuesBegin/AddNonStreamValues/End` | 🟡 **gRPC Begin/End LIVE-VERIFIED 2026-06-21; precondition CORRECTED 2026-06-21** (WCF still blocked — D2). The earlier "missing console session" step was **disproved live**: `StorageService.OpenStorageConnection` returns `type=4 code=85` ("session not registered") for every param — it's the storage engine's SF/snapshot channel (separate `GrpcStorageClient`/service identity), not a front-door client op. The real precondition is **front-door `HistoryService.RegisterTags`** (RTag2-family) for the target tag — the R3.1 batch failed at `AddNonStreamValues` *because the tag wasn't registered*. Remaining: capture the regular-tag `RegisterTags` `btTagInfos` (only CM_EVENT's tag-GUID form is known) + the `AddNonStreamValues` `btInput`. See [`revision-write-path.md`](revision-write-path.md) §R3.1 follow-up. |
| R3.2 | `AddHistoricalValuesAsync` | batched begin→values→end | 🟡 architecturally unblocked; precondition now correctly identified (front-door `RegisterTags`, not `OpenStorageConnection`); needs a native gRPC capture of the regular-tag `RegisterTags` `btTagInfos` + the `AddNonStreamValues` `btInput`, then a real `bCommit=true` write/read-back |
| R3.3 | Ingest-permission validation | confirm the target accepts original-data insert (distinct from `AddS2` cache wall) | ✅ **distinct on gRPC** — Begin succeeded against a real write-enabled session (the WCF/native cache gate does not apply here) |
**Acceptance:** historical points inserted and read back. **WCF path closed (D2).** gRPC path:
@@ -330,5 +330,5 @@ event-send). M3/M4 as demand dictates.
| M0 gRPC parity + capture tooling | foundation | M | unblocks everything, Windows-free | ✅ **done** |
| M1 cheap surface | TRIVIAL/BOUNDED | ML | most remaining read/config | ✅ **done** (reachable surface; rest bounded out) |
| M2 event send | CAPTURE | SM | headline write capability | ✅ **done** |
| M3 historical writes | BOUNDED | M | backfill | 🟡 **gRPC Begin/End live-verified + full sequence mapped (2026-06-21)**; WCF blocked (D2). Follow-up: OpenStorageConnection handshake + `btInput` decode → commit+read-back |
| M3 historical writes | BOUNDED | M | backfill | 🟡 **gRPC Begin/End live-verified (2026-06-21); precondition corrected** — front-door `HistoryService.RegisterTags` (not `OpenStorageConnection`, which is the SF-channel dead end / error 85). WCF blocked (D2). Follow-up: capture regular-tag `RegisterTags` `btTagInfos` + `AddNonStreamValues` `btInput` → commit+read-back |
| M4 SF / revisions / redundancy | HARD | L×N | parity completeness | defer (R4.2 = same pipe wall) |