D2 (revision-write): probe SysTimeSec — same gate, narrower scope

Extended the harness with --write-revision-target-tag <name> (overrides
the value's TagKey via SQL lookup) and --write-revision-skip-validate
(passes false to AddNonStreamedValue's `validate` boolean). Added
--write-revision-commit gate so the harness validates without actually
calling SendValues by default — important when targeting system tags.

Probed SysTimeSec (wwTagKey=12, server-cache-resident system tag):
- AddNonStreamedValue: ErrorCode=TagNotFoundInCache (129) — same failure
- With validate=false: same failure (the cache check is intrinsic, not
  gated by the boolean)

Conclusion: the gate is per-(client-session, tag), not per-server-cache.
Even tags the SERVER cache knows about are rejected because the LIBRARY
maintains a separate per-connection tag list that AddNonStreamedValue
checks. That list isn't populated by knowing the wwTagKey alone — it
needs whatever mechanism (RegisterTags2 / read flow side effect / IO
server registration) that we haven't reverse-engineered.

The revision-write path remains architecturally blocked for managed
clients. Plan doc updated with the SysTimeSec finding.

177/177 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 02:27:58 -04:00
parent 2feb56d52c
commit 3af8a13059
2 changed files with 93 additions and 11 deletions
+50 -9
View File
@@ -42,16 +42,57 @@ flow.
## Conclusion
The revision-write path **does not bypass the AddS2 blocker** — it
shares the same `TagNotFoundInCache` precondition. There is no path
from a managed client to a successful AddNonStreamValues call against
a client-created sandbox tag.
shares the same `TagNotFoundInCache` precondition.
To validate this conclusion further (not done in this pass — too risky
for the production Historian) one could try AddNonStreamedValue against
a system tag like `SysTimeSec` whose key IS in the cache from upstream
registration. If that succeeds, the path is implementable in principle
for IO-registered tags; if it also fails, the prerequisite is even
stricter.
### Follow-up probe (2026-05-05): SysTimeSec
To narrow the gate's scope, the harness was extended with
`--write-revision-target-tag <name>` (overrides the value's TagKey via
SQL lookup). Probed `SysTimeSec` (an auto-populated system tag whose
wwTagKey=12 is well-known in the runtime cache):
```
AddNonStreamedValue (TagKey=12 SysTimeSec):
Result=False
ErrorCode=TagNotFoundInCache
ErrorDescription="error = 129 (Tag not found in cache)"
```
Same failure. Then probed with `--write-revision-skip-validate` to set
the `validate` boolean to false on `AddNonStreamedValue` — same
`TagNotFoundInCache` failure. The cache check is intrinsic to the
function, not gated by the `validate` parameter.
So the gate is **per-(client-session, tag)**, not per-(server-cache, tag):
- Server-side, `SysTimeSec` IS in the runtime cache (it's auto-populated).
- Client-side, the managed library has its own per-connection tag list
that AddNonStreamedValue checks. That list is NOT populated by simply
knowing the wwTagKey — something else (likely a `RegisterTags2` call
during connection open, or the read flow as a side effect, or
IO-server-driven registration) populates it.
The harness opens with `ReadOnly=false` for the write scenario, which
may suppress the read-flow side effect that would otherwise populate
the local cache. Without further RE on what populates the local cache,
no path is reachable for a managed client to write either streaming or
revision values.
### Decisive blocker
Both `AddStreamedValue` (AddS2) and `AddNonStreamedValue` (revision
write) hit the same client-side cache gate. That gate isn't bypassed by:
1. Using a real wwTagKey from SQL
2. Targeting a server-cache-resident tag (SysTimeSec)
3. Setting `validate=false` on AddNonStreamedValue
There is no managed-client path to a successful write against this
server architecture without first reverse-engineering and exercising
whatever populates the per-connection local cache. That's a much larger
investigation — likely involving the WCF `RegisterTags2` op,
HistorianClient C++ internals, and/or IO-server-driven cache
registration that managed clients can't trigger directly.
## Decision