docs(raw): live /run gate PASSED for the raw-modal picker live config; records Gitea #539

All three legs pass against the patched AbCip fixture: the edit modal and the
manual-entry grid both browse the live nine-tag symbol table, and an unreachable
device degrades to the graceful error chip while the modal still saves.

The gate also found a defect unrelated to this change and filed it as #539: the
AbCip discovery path (LibplctagTagEnumerator, LibplctagTemplateReader) passes
libplctag the bare host, so an authored non-default EtherNet/IP port is dropped
and the browse reaches whatever answers on 44818 — #534's defect surviving in the
discovery half. Discovered because the first negative-leg endpoint differed from
the fixture only by port, which never reached the wire.
This commit is contained in:
Joseph Doherty
2026-08-05 04:32:38 -04:00
parent d88e42b8c4
commit 8af5742406
3 changed files with 52 additions and 6 deletions
@@ -1160,8 +1160,10 @@ neither agreed with a controller. Fixed, with the layout now pinned by a capture
passing a `"{}"` stub as `GetDriverConfigJson`). Fixed by resolving the merged Driver+Device
config (`IRawTreeService.LoadMergedProbeConfigAsync` — the same blob Test-connect and
`RawBrowseModal` dial) once per open and forwarding a live accessor; fail-soft on resolve
failure. See `2026-08-05-raw-modal-picker-live-config-design.md` for the design and outcome
(including the stale-open race the review found and closed).
failure. **Live-gated 2026-08-05, all three legs PASSED** against the same patched AbCip fixture
this plan's second gate used. See `2026-08-05-raw-modal-picker-live-config-design.md` for the
design and outcome (including the stale-open race the review found and closed, and Gitea **#539**
— the AbCip discovery path dropping the authored EtherNet/IP port — which that gate uncovered).
## Out of scope (explicitly)
@@ -113,7 +113,12 @@ marginal cost.
fix surfaced and closed a further stale-open race (a superseded open could install another
device's config); `RawBrowseModal` shares that pre-existing race shape and is recorded in
`deferment.md` §0.1, not fixed here. Pinned by `RawTagModalMergedConfigTests` +
`RawManualTagEntryModalMergedConfigTests`.
`RawManualTagEntryModalMergedConfigTests`, and **live-gated 2026-08-05** — all three legs PASSED
against the patched AbCip fixture (edit modal + manual entry both browse the nine-tag tree; an
unreachable device degrades to the error chip and still saves). The gate also found **Gitea #539**:
the AbCip *discovery* path drops the authored EtherNet/IP port, so a device on a non-default port
browses whatever answers on 44818 — #534's defect surviving in `LibplctagTagEnumerator` +
`LibplctagTemplateReader`.
---
@@ -1,8 +1,9 @@
# Raw-modal address pickers: pass live merged config to browse — design
**Status:** IMPLEMENTED 2026-08-05 (unit suite green, AdminUI 1064/1064; live `/run` gate §4.2
pending). Closes the last dangling follow-up from the #468 universal-browser live gate
("driver-page address pickers pass no live config"). Outcome notes in §6.
**Status:** DONE 2026-08-05 — unit suite green (AdminUI 1064/1064) and the live `/run` gate §4.2
**PASSED** (all three legs; see §7). Closes the last dangling follow-up from the #468
universal-browser live gate ("driver-page address pickers pass no live config"). Outcome notes in
§6; gate evidence + the defect it uncovered (Gitea **#539**) in §7.
## 1. Problem
@@ -122,3 +123,41 @@ through a spec review + code review. Things the reviews changed or decided:
- The liveness of the forwarded `Func` is load-bearing (the pickers call it at click time, after
the async resolve) and is pinned in both files by retained-delegate facts, each falsified against
a deliberate capture-by-value implementation before being trusted.
## 7. Live `/run` gate — PASSED (2026-08-05)
Run by the main session against the local docker-dev rig (`http://localhost:9200`, login disabled)
with **both** central containers rebuilt from master `d88e42b8` and verified on the same image
(`sha256:6b1ddb47…`) — the previously-running image predated the merge by three minutes, and `:9200`
round-robins central-1/central-2, so per-container image parity is the precondition for reading
anything into the result. Backend: the #468 patched `ab_server` (`otopcua-ab-server-controllogix`,
`10.100.0.35:44818`), authored as `docker-dev / abcip-browse / Device1`.
| Leg | Result |
|---|---|
| **Edit modal** — `Device1/Motor_Run` → Tag config → Build → Browse controller | ✅ `BROWSER OPEN`, tree `AbCip → Device1 → Discovered` with **all nine** fixture tags (`Motor_Run`, `Line1_Count`, `Pump_1_Speed`, `TestArray`, `TestString`, `TestSINT`, `TestBOOL`, `TestREAL`, `TestDINT`). Selecting `TestREAL` landed it in the Tag-path field. |
| **Manual entry** — device → Add tags (manual) → row Configure → Build → Browse controller | ✅ Same nine-tag tree, same `Discovered` shape. |
| **Negative** — a device whose endpoint is unreachable | ✅ Button still renders; the click yields the graceful `ERRORTIMEOUT` chip, the grid stays usable, and the row **still saved** (a tag was created after the failed browse). |
The tree's second level is labelled with the **device's own name** (`Device1`, and `DeadDevice` for
the throwaway negative-leg device) — direct evidence that the per-device merged config, not a stub,
is what the picker captures against. Under the old `"{}"` stub no endpoint reached the capture at
all. The probe device + its tag were deleted afterwards; the rig is back to its pre-gate state.
### The gate found a real defect — Gitea #539 (AbCip discovery drops the authored port)
The negative leg was first run against `ab://10.100.0.35:44999/1,0` (nothing listening on 44999) and
**browsed successfully**, returning the fixture's nine tags — while **Test Connect on the same merged
config correctly failed** `CONNECT FAILED: CONNECTIONREFUSED`. That divergence is not this change: the
AbCip discovery path passes libplctag the bare host, so libplctag substitutes its own 44818 default —
Gitea **#534**'s defect surviving in `LibplctagTagEnumerator.cs:33` and `LibplctagTemplateReader.cs:24`
(the data path was fixed and uses `LibplctagGateway`). Consequence: browsing a device on a non-default
port shows **whatever answers on 44818**, which on a plant network is another PLC.
Re-pointing the device at `ab://127.0.0.1/1,0` produced the expected failure, which is the run
recorded in the table above. Two things worth carrying forward:
- ⚠️ **A "bad endpoint" chosen by changing only the port is not a valid negative control for
anything libplctag-backed** — the port may never reach the wire. Change the **host**.
- The fixture sitting on the default 44818 is exactly why #534's discovery half survived its own
fix: on 44818 the bug is invisible by construction, so no existing test could see it.