diff --git a/CLAUDE.md b/CLAUDE.md index e181aa94..dfd6862e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -315,6 +315,11 @@ When the section is disabled the applier binds the no-op `NullHistorianProvision run. Every dispatch logs a tally (`dispatched/requested/ensured/skipped/failed`); `dispatched=N` with `requested=0` flags the dormant no-op. The API key must carry `historian:tags:write` for `EnsureTags`. (PR #423 shipped `GatewayTagProvisioner` but left this wiring out, so provisioning was dormant; restored 2026-06-27.) +**0.2.0 DataType delta (archreview 06/U-7):** `EnsureTags` is an **upsert** (create-or-update, not +skip-if-exists), and the 0.2.0 `HistorianTagDefinition.DataType` proto3-optional change means Boolean tags now +provision as **Int1** (0.1.0 silently defaulted them to Float). A pre-bump Float Boolean is asked to retype +on its next deploy; the AVEVA-side retype policy is pinned by the live retype probe — see the "0.2.0 DataType +provisioning delta" section in `docs/Historian.md`. ### Gateway-side prerequisites @@ -349,6 +354,7 @@ export HISTGW_GATEWAY_ENDPOINT=https://wonder-sql-vd03:5222 # absolute gateway export HISTGW_GATEWAY_APIKEY=histgw__ # must carry historian:read + historian:write (+ tags:write) scopes export HISTGW_TEST_TAG= # read round-trip export HISTGW_WRITE_SANDBOX_TAG= # e.g. HistGW.LiveTest.Sandbox — write round-trip (EnsureTags + write) +export HISTGW_BOOL_SANDBOX_TAG= # e.g. HistGW.LiveTest.BoolSandbox — 0.2.0 Boolean EnsureTags leg + retype probe (06/U-7) export HISTGW_ALARM_SOURCE= # alarm SendEvent → ReadEvents round-trip dotnet test --filter "Category=LiveIntegration" ``` diff --git a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json index 54febf58..19e36248 100644 --- a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json +++ b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json @@ -65,7 +65,7 @@ { "id": "T9", "subject": "U-7 docs: migration/assessment note (EnsureTags is upsert; pre-bump Float Booleans; three retype hypotheses; failed=N tally observability) in docs/Historian.md + CLAUDE.md HISTGW_BOOL_SANDBOX_TAG recipe update; commit 2 (U-7) after T7-T9", - "status": "pending", + "status": "completed", "blockedBy": [ "T8" ] diff --git a/docs/Historian.md b/docs/Historian.md index e97e9ba3..183ef1b7 100644 --- a/docs/Historian.md +++ b/docs/Historian.md @@ -129,6 +129,35 @@ for this node), which is the signal to check that `ServerHistorian:Enabled=true` > so a HistoryRead on it runs to the full `CallTimeout` (~30 s) before erroring. Auto-provisioning on deploy > keeps freshly-historized tags from hitting that slow path. See the `CallTimeout` row above. +### 0.2.0 DataType provisioning delta (archreview 06/U-7) + +`EnsureTags` is contract-documented (HistorianGateway 0.2.0) as an **upsert** — *"creates or updates +historian tags according to the given definitions"* — that maps per-tag onto AVEVA's +`HistorianClient.EnsureTagAsync(definition) -> bool`. It is **not idempotent** (not routed through the +idempotent retry pipeline) and **not skip-if-exists**. + +The 0.1.0→0.2.0 client bump made `HistorianTagDefinition.DataType` **proto3-optional** (explicit presence). +Under 0.1.0, the provisioner's `Boolean → Int1` mapping sent wire value `0`, indistinguishable from unset, so +the gateway defaulted such tags to the SDK-default **Float**. Under 0.2.0 the same call carries explicit +presence, so Boolean tags now provision as **Int1** (the pre-existing intent of `HistorianTypeMapper` — no +mapper code change is needed or wanted). Consequences: + +1. **New Boolean tags provision correctly as Int1.** This is the fix arriving as a silent, zero-diff + semantic change from the bump. +2. **Pre-bump Boolean tags exist historian-side as Float.** On the next deploy that dispatches provisioning + for such a tag, the gateway is asked (via the upsert) to update it to Int1. The AVEVA-side outcome of an + in-place analog retype is the one thing the contract does not pin; the candidate behaviors are + (i) **accepted with tag versioning** (history preserved under the old version, new writes under Int1), + (ii) **accepted as a plain retype**, or (iii) **rejected per-tag** → `TagOperationResult.Success=false` + → counted in the provisioner's `failed=N` dispatch tally. Only a live run distinguishes these — see the + **retype probe** (`EnsureTags_type_change_outcome_is_observable`) in the [Live /run gate](#live-run-gate). + *(This list is replaced with the observed answer after the first live run.)* +3. **The failure mode is bounded and observable either way.** Provisioning is fire-and-forget and never + blocks/fails a deploy; a per-tag rejection surfaces only as the tally's `failed` count. Values written to + a still-Float tag by the recorder remain readable (Float supersets Int1's 0/1 range); nothing is lost + pending a reconcile. A bulk retype sweep is **not** done in code — assess via the probe first, then decide + whether reconciliation is a one-time operator action. + --- ## HistoryRead behavior @@ -350,7 +379,14 @@ AVEVA Historian behind it healthy. Set `ServerHistorian:Enabled=true` with the c least one historized Galaxy tag. The gate is operator-driven — it is not part of the local docker-dev rig. The gateway-backed driver also ships an env-gated live suite (`Category=LiveIntegration`); see the `HISTGW_GATEWAY_ENDPOINT` / `HISTGW_GATEWAY_APIKEY` / `HISTGW_TEST_TAG` / `HISTGW_WRITE_SANDBOX_TAG` / -`HISTGW_ALARM_SOURCE` env vars (it skips cleanly when they are absent). +`HISTGW_BOOL_SANDBOX_TAG` / `HISTGW_ALARM_SOURCE` env vars (it skips cleanly when they are absent). +`HISTGW_BOOL_SANDBOX_TAG` (e.g. `HistGW.LiveTest.BoolSandbox`, a **dedicated** writable Boolean tag — +never the shared Float write sandbox) drives the 0.2.0 Boolean `EnsureTags` leg +(`EnsureTags_boolean_sandbox_provisions_as_Int1`) and the retype probe +(`EnsureTags_type_change_outcome_is_observable`) that resolve the +[0.2.0 DataType provisioning delta](#020-datatype-provisioning-delta-archreview-06u-7) open question. Running +the full suite against a **0.2.0** gateway (all six tests, none skipped) also discharges 06/U-2's +"full documented run" for the historian suite. See [AlarmHistorian.md](AlarmHistorian.md) for the alarm write path and [ServiceHosting.md](ServiceHosting.md) for the (external) HistorianGateway deployment.