docs(audit): AbServer-Test-Fixture.md — accuracy pass

STRUCTURAL: links-report.md row — path MISSING src/tools/ab_server/.
ab_server is not in this repo; it lives in the upstream libplctag/libplctag
GitHub repo and is cloned + built inside Docker/Dockerfile. Rewrote Binary
bullet to describe it as an external upstream source (no local path reference
that fails the link checker).

STALE-STATUS: Lifecycle TCP-probe host was listed as 127.0.0.1:44818
(AbServer-Test-Fixture.md:21). AbServerFixture.cs:35,72 default is
10.100.0.35:44818 (shared Docker host, migrated 2026-04-28). Fixed.

CODE-REALITY: Micro800 profile Notes quoted "ab_server has no --plc micro800
— falls back to controllogix emulation." Incorrect: Docker/docker-compose.yml
micro800 service uses --plc=Micro800; AbServerProfile.cs:49 confirms
"--plc=Micro800 mode (unconnected-only, empty path)." Updated Notes quote
and summary table row to match actual compose behaviour.

Verified: python3 .docs-audit/check_links.py — zero rows for this doc.
This commit is contained in:
Joseph Doherty
2026-06-03 16:01:06 -04:00
parent 33d40901d2
commit 497d8be1d5
+19 -13
View File
@@ -10,17 +10,20 @@ quirk. UDT / alarm / quirk behavior is verified only by unit tests with
## What the fixture is
- **Binary**: `ab_server` — a C program in libplctag's
`src/tools/ab_server/` ([libplctag/libplctag](https://github.com/libplctag/libplctag),
MIT).
- **Binary**: `ab_server` — a C program from the upstream
[libplctag/libplctag](https://github.com/libplctag/libplctag) repository
(MIT license). It is **not** part of this repo's source tree; `Docker/Dockerfile`
clones libplctag at a pinned tag and builds the `ab_server` CMake target in a
multi-stage build.
- **Launcher**: Docker (only supported path). `Docker/Dockerfile`
multi-stage-builds `ab_server` from source against a pinned libplctag
multi-stage-builds `ab_server` from source by cloning libplctag at a pinned
tag + copies the binary into a slim runtime image.
`Docker/docker-compose.yml` has per-family services (`controllogix`
/ `compactlogix` / `micro800` / `guardlogix`); all bind `:44818`.
- **Lifecycle**: `AbServerFixture` TCP-probes `127.0.0.1:44818` at
collection init + records a skip reason when unreachable. Tests skip
via `[AbServerFact]` / `[AbServerTheory]` which check the same probe.
- **Lifecycle**: `AbServerFixture` TCP-probes `10.100.0.35:44818` (the shared
Docker host) at collection init + records a skip reason when unreachable.
Tests skip via `[AbServerFact]` / `[AbServerTheory]` which check the same
probe.
- **Profiles**: `KnownProfiles.{ControlLogix, CompactLogix, Micro800, GuardLogix}`
in `AbServerProfile.cs` — thin Family + ComposeProfile + Notes records;
the compose file is the canonical source of truth for which tags get
@@ -71,12 +74,15 @@ Unit coverage: `AbCipAlarmProjectionTests` — fakes feed `InFaulted` /
### 3. Micro800 unconnected-only path
Micro800 profile `Notes`: *"ab_server has no --plc micro800 — falls back to
controllogix emulation."*
Micro800 profile `Notes`: *"--plc=Micro800 mode (unconnected-only, empty path).
Driver-side enforcement verified in the unit suite."*
The empty routing path + unconnected-session requirement (PR 11) is unit-tested
but never challenged at the CIP wire level. Real Micro800 (2080-series) on a
lab rig would be the authoritative benchmark.
The compose service boots `ab_server --plc=Micro800` with an empty routing path.
The unconnected-session requirement (PR 11) is validated at the driver unit-test
level via `FakeAbCipTagRuntime`; the wire-level contract (what happens when
a connected-send arrives at a real Micro800 backplane) is not exercised by the
simulator. Real Micro800 (2080-series) on a lab rig would be the authoritative
benchmark.
### 4. GuardLogix safety subsystem
@@ -177,7 +183,7 @@ project is authored.
| "Is my atomic read path wired correctly?" | yes | yes | yes | yes |
| "Does whole-UDT grouping work?" | no | yes | **yes** | yes |
| "Do ALMD alarms raise + clear?" | no | yes | **yes** | yes |
| "Is Micro800 unconnected-only enforced wire-side?" | no (emulated as CLX) | partial | yes | yes (required) |
| "Is Micro800 unconnected-only enforced wire-side?" | partial (--plc=Micro800 boots, but wire rejection untested) | partial | yes | yes (required) |
| "Does GuardLogix reject non-safety writes on safety tags?" | no | no | yes (Emulate 5580) | yes |
| "Does CompactLogix refuse oversized ConnectionSize?" | no | partial | yes (5370 firmware) | yes |
| "Does BOOL-in-DINT RMW race against concurrent writers?" | no | yes | partial | yes (stress) |