From b25279dbbd238da3da8c6e05db6c68c3f99d9e0f Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 5 Aug 2026 20:26:00 -0400 Subject: [PATCH] docs: live-gate record for #541 on docker-dev (PASS; found the 0-tags defect) --- ...-08-05-skipped-tag-visibility-live-gate.md | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 docs/plans/2026-08-05-skipped-tag-visibility-live-gate.md diff --git a/docs/plans/2026-08-05-skipped-tag-visibility-live-gate.md b/docs/plans/2026-08-05-skipped-tag-visibility-live-gate.md new file mode 100644 index 00000000..8410f7e3 --- /dev/null +++ b/docs/plans/2026-08-05-skipped-tag-visibility-live-gate.md @@ -0,0 +1,94 @@ +# Live gate — skipped-tag visibility (Gitea #541) + +**Date:** 2026-08-05 +**Rig:** local `docker-dev/` (OrbStack), MAIN pair `central-1` / `central-2`, AdminUI `http://localhost:9200` +**Build under test:** `fix/541-skipped-tag-visibility` +**Result: PASS**, and the gate found one defect that unit tests did not (§4). + +## What needed proving + +The unit suites pin the ledger semantics, the publish fingerprint, the proto round-trip and the +view-model. None of them prove the thing an operator actually depends on: that the **Tags** column on +`/hosts` renders the right cell, in the right colour, with actionable detail, against a driver that is +genuinely skipping tags on a real deployment. + +## Method + +The rig already carried eleven driver instances. Three of the thirteen tags on the `pymodbus` Modbus +driver had their `TagConfig.region` rewritten to `NoSuchRegion` — the realistic shape of a bad blob — +then MAIN was redeployed via `POST /api/deployments`. Backup of the original thirteen blobs taken +first; all three restored and redeployed at the end (§5). + +## Results + +Driver logs confirmed the skips reached the driver: + +``` +[WRN] Modbus tag config did not map to a definition; skipping. Driver=MAIN-modbus RawPath=pymodbus/plc/Probe3 +[WRN] Modbus tag config did not map to a definition; skipping. Driver=MAIN-modbus RawPath=pymodbus/plc/Probe4 +[WRN] Modbus tag config did not map to a definition; skipping. Driver=MAIN-modbus RawPath=pymodbus/plc/Probe5 +``` + +`/hosts` then rendered — note every other column on the `pymodbus` row reads perfectly healthy, which +is the entire point: + +| Driver | Status | Errors/5 min | Last error | Last read | **Tags** | Chip | +|---|---|---|---|---|---|---| +| `pymodbus` | HEALTHY | 0 | — | advancing | **3 / 13 SKIPPED** | `chip-warn` (amber) | +| `sql-oracle-fixture` | HEALTHY | 0 | — | advancing | **1 / 1 SKIPPED** | `chip-bad` (red) | +| `sql-postgres-fixture` | HEALTHY | 0 | — | advancing | **1 / 1 SKIPPED** | `chip-bad` (red) | +| `gate-modbus-renamed` | HEALTHY | 0 | — | advancing | **1 / 1 SKIPPED** | `chip-bad` (red) | +| `abcip-browse` | HEALTHY | 0 | — | advancing | 5 tags | none | +| `adsfa` (Calculation) | HEALTHY | 0 | — | — | 0 tags | none | +| `opcua1` | FAULTED | 1 | (config error) | — | — | none | +| `rtu-gate` | FAULTED | 1 | Connection refused | — | — | none | + +Tooltip on the amber chip, verbatim: + +> 3 of 13 authored tag(s) are not being polled. They report BadNodeIdUnknown and the driver's state +> does not reflect them. pymodbus/plc/Probe3: TagConfig did not map to a definition · +> pymodbus/plc/Probe4: … · pymodbus/plc/Probe5: … + +and on a red one: + +> This driver is polling NONE of its 1 authored tag(s). … + +**Three of the four skipping drivers were not planted.** `sql-oracle-fixture`, `sql-postgres-fixture` +and `gate-modbus-renamed` were already misconfigured on this rig and had been silently skipping their +tags for an unknown length of time, each reporting Healthy with zero errors. The column surfaced them +on its first run — an unplanned but fairly direct demonstration of the defect being real rather than +theoretical. + +Restoring the three blobs and redeploying returned `pymodbus` to `13 tags` with no chip, which also +exercises the clear-on-clean-rebuild path live. + +## 4. Defect the gate found — `0 tags` on a driver that never built a table + +The first pass rendered **`0 tags`** for `opcua1` and `rtu-gate`. Both are FAULTED, both have **two +authored tags** in the ConfigDb, and neither ever completed `InitializeAsync` — so their ledgers had +never committed and `Snapshot()` returned `SkippedTagReport.Empty`. + +A zero report is a **positive claim** — *"this driver skipped nothing"* — and making it on behalf of a +driver that never got as far as looking is precisely the quiet inaccuracy #541 exists to remove. Not +caught by unit tests because every one of them drove a driver through a completed build first. + +Fixed by making `ISkippedTagReporter.GetSkippedTags()` and `SkippedTagLedger.Snapshot()` return +**null until the first committed build**, so the column shows the same em-dash it already used for a +driver that cannot report at all. Both are *no answer*; neither is *nothing skipped*. Pinned by three +new `SkippedTagLedgerTests` cases plus a corrected FOCAS assertion. Re-gated on a rebuilt image: both +faulted drivers now render `—`, while `adsfa` — which genuinely has zero authored tags and **did** +build — correctly still renders `0 tags`. That pair is the discriminating case. + +## 5. Rig left as found + +The three `pymodbus` blobs were restored from the pre-change backup and MAIN redeployed; `/hosts` +shows `13 tags`. The three pre-existing skipping drivers were left exactly as they were — they are the +rig's own configuration, not this gate's. + +## Not proven here + +- The `+N more not shown` truncation note (needs a deployment skipping more than + `SkippedTagLedger.MaxSample` = 20 tags; unit-covered only). +- The central-side proto path — the rig's central pair is fused admin+driver, so the health snapshot + never crossed the Phase 5 gRPC telemetry stream. `TelemetryProtoMapCentral`/`Node` are unit-covered + round-trip only.