# Plan: Speculative Items Sweep (2026-05-04) The five items I previously called out as speculative / deferred. Goal: exercise the cheap ones, investigate the medium ones to feasibility, and write sub-plans for anything too big to ship in one push. ## Items | ID | Item | Effort | Touches | |---|---|---|---| | C3 | Expose `IntegralDivisor` on `HistorianTagDefinition` | small | `HistorianTagDefinition.cs`, `HistorianTagWriteProtocol.cs`, orchestrator, tests | | E | Unit tests for `AllowUntrustedServerCertificate` / `ServerDnsIdentity` | small | new test file under `tests/` | | D3 | Root-cause Discrete/String/Int1/Int8/UInt8 EnsT2 failure | medium (investigation) | native harness, possibly serializer | | D1 | Capture wire bytes for `AddTagExtendedProperties` | medium (capture + decode) | native harness, possibly new serializer + public API | | D2 | Implement `AddRevisionValuesBegin/Value/End` (revision-write path) | large | new orchestrator + 3 new public APIs | ## Parallelism Concurrency-safe groupings (each pair is independent at the file level): - **C3 ↔ E** — C3 touches `HistorianTagDefinition.cs` + `HistorianTagWriteProtocol.cs` + orchestrator + integration tests; E adds a new test file + might add a small unit-test util. No file overlap. - **D3 ↔ D1** — Both touch the native trace harness Program.cs, so they conflict if done concurrently. Sequence them. - **C3/E ↔ D3/D1** — No file overlap; can run concurrently with the harness work. - **D2** stands alone (different code paths entirely). In a single-agent session, the order is: 1. C3 (small, predictable) — land first 2. E (small, predictable) — land second 3. D3 (investigation; documents findings whether or not implementation is possible) 4. D1 (investigation + capture; same pattern) 5. D2 — write a focused sub-plan; do NOT implement in this sweep ## Success Criteria - C3: `HistorianTagDefinition.IntegralDivisor` (default 1.0) plumbed through serializer; unit test asserts non-default value flips the wire bytes; live test asserts the value persists in `Tag.IntegralDivisor` (or wherever it lands in SQL). - E: 2-3 unit tests asserting `HistorianWcfClientCredentialsHelper.Configure` and `HistorianWcfBindingFactory.CreateEndpointAddress` honor the new options. - D3: documented root cause + decision (workable path / not workable / requires further capture). If a workable path emerges quickly, also implement. - D1: documented evidence summary + decision (worth implementing / defer / requires customer ask). - D2: `docs/plans/revision-write-path.md` (or similar) with the 5-step capture sequence + open questions. ## Findings ### C3 — IntegralDivisor (executed 2026-05-05) Plumbed through serializer + orchestrator; default 1.0. Unit test verifies the 8-byte double immediately preceding the trailer flips with a non-default value. Live probe: server accepts the wire bytes but `IntegralDivisor` appears to be stored on `EngineeringUnit` (shared across all tags using that EU) rather than per-tag, and the EU's stored value didn't change for the test. Documented in the property's doc-comment. No live integration test added (nothing to assert in SQL). ### E — Cert option unit tests (executed 2026-05-05) Added `HistorianWcfCertOptionTests` (5 tests) covering: - `Configure` is a no-op when `AllowUntrustedServerCertificate=false` - `Configure` installs the accept-any validator + `RevocationMode.NoCheck` when the option is true - `CreateEndpointAddress` with no DNS identity returns an address with `Identity == null` - `CreateEndpointAddress` with a DNS identity attaches a `DnsEndpointIdentity` - `CreateBindingPair(RemoteTcpCertificate)` propagates `ServerDnsIdentity` to the History endpoint (and not to the Retrieval endpoint, which uses plain MdasNetTcp without TLS) ### D3 — Discrete/String/Int1/Int8/UInt8 EnsT2 root cause (investigated 2026-05-05) Probed each unsupported type via the native trace harness with `--write-data-type {Type}`. Result for SingleByteString and Int1 (others truncated in the same output): - `HistorianAccess.AddTag` returns `Success=false`, `TagKey=0` - Error: `ErrorCode=ValidationFailed`, `ErrorType=CustomError`, `ErrorDescription="Transaction validation failed"` **Conclusion:** The failure is **server-side**, not wire-format. The `/Hist.EnsT2` server-side validator rejects non-analog types when invoked through the `AddTag → EnsT2` code path. To unlock these types from the SDK we'd need to: 1. Capture a successful native creation of a discrete/string tag via some other mechanism (likely SMC's tag-import path or a different WCF op like `AddTagExtendedProperties` carrying the discrete/string-specific metadata) 2. Diff the working native flow against the failing one to see what ancillary fields the validator expects (TagType vs CDataType, separate StorageType, IO-server pre-registration, etc.) **Decision:** defer until a customer asks. The native AVEVA wrapper itself cannot create these tags via `AddTag` from a managed client — implementing this would require RE work on a path the wrapper doesn't exercise, which is much higher risk than the existing analog write surface. ### D1 — AddTagExtendedProperties feasibility (investigated 2026-05-05) Managed surface confirmed. Native API: - Public managed entry point: `ArchestrA.HistorianAccess.AddTagExtendedProperties` (token `0x0600619B`, 140 IL instructions, 6 locals) - WCF op: `CHistoryConnectionWCF.AddTagExtendedPropertyGroups` (token `0x0600405C`) - Underlying contract method: `IHistoryServiceContract2.AddTagExtendedProperties` (already declared in our reproduced contracts) - Managed input type: `HistorianTagExtendedPropertyGroup` wrapping the native `CTagExtendedPropertyGroup` C++ class. Built from a `std::vector` (visible in the IL locals). Property group structure not yet decoded. **Decision:** defer implementation. Cost estimate: 1. Reflect-construct `HistorianTagExtendedPropertyGroup` via the native harness (probably 2-4 hours — these C++/CLI types often have hidden constructor requirements that surface only at runtime). 2. Call `AddTagExtendedProperties` with a sandbox group; capture wire bytes via `instrument-wcf-writemessage` (1 hour). 3. Decode the `CTagExtendedPropertyGroup` payload — this is its own struct that needs walking field-by-field against the native serializer IL (token `0x06002038`, `CHistStorage.AddTagExtendedProperties`) (3-6 hours). 4. Implement managed `HistorianTagExtendedPropertyGroup` model + serializer + public `AddTagExtendedPropertiesAsync` API + tests (4-6 hours). Total: 1-2 days of focused work. Defer until a customer asks for tag extended properties or the analog write surface needs them as a prerequisite. ### D2 — AddRevisionValuesBegin/Value/End Sub-plan deferred to a dedicated session — see `docs/plans/revision-write-path.md` (created in this sweep). ## Out of scope for this sweep Refactoring `HistorianWcfTagClient` to respect `options.Transport` for browse / metadata (i.e., let it use cert binding from Windows). Worth doing but not part of the speculative-items list.