A first-touch native write to a T/C/R status-bit sub-element (T4:0.DN) created
and initialised the tag's runtime before the runtime's write refusal could fire;
the init is one READ, so against a device lacking the file (the ab_server
fixture has no T/C/R) it surfaced as BadCommunicationError + Degraded health
instead of BadNotSupported. The bit-RMW path was not involved: AbLegacyAddress
rejects sub-element + /N, so a status bit's BitIndex is always null.
NativeAbLegacyTagRuntime.WriteRefusal is now the single predicate (status bit
by address whatever the authored type; any PLC-5 write), asked by the driver via
IAbLegacyTagFactory.WriteRefusal (default null, so libplctag is unchanged)
before EnsureTagRuntimeAsync and ahead of the bit-RMW branch.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
The AbLegacy integration suite has never run against a simulator.
/opt/otopcua-ablegacy/ did not exist on the Docker host — the stack was written
but never deployed — and every ab_server across BOTH ab fixtures bound :44818.
So the AB CIP fixture's ControlLogix container (up for two weeks) answered this
suite's TCP reachability probe, the suite declined to skip, and it ran SLC500-mode
PCCC against a ControlLogix. Four failures that read as "fixture missing".
Two structural problems, both fixed here:
- The theory parameterises over all three PCCC families, but only one container
could ever exist on the shared port, so at least two cases failed on every
conceivable run. The suite was un-greenable by construction, and
AB_LEGACY_COMPOSE_PROFILE existed to paper over it by filtering the theory
down to whichever container happened to be up.
- A reachability probe that cannot tell one simulator from another is not a skip
gate. It answered "is some ab_server listening", which is a different question
from the one the caller is asking, and reported the difference as a test
failure.
Each family now has its own host port — 44819 slc500, 44820 micrologix, 44821
plc5 — so all three run together and alongside AbCip on 44818 (ab_server still
listens on 44818 inside each container; only the host mapping differs). Skip is
per-family via SkipReasonFor(profile); the class-level gate runs if ANY family is
up so one down container cannot silently skip the other two.
AB_LEGACY_COMPOSE_PROFILE now applies only in AB_LEGACY_ENDPOINT-pinned
real-hardware mode, where the family genuinely is a property of the one device.
This finally makes true what the fixture's XML doc has claimed from the start —
that it and the AB CIP fixture can point at different containers simultaneously.
Deployed to /opt/otopcua-ablegacy/ on 10.100.0.35; all three containers running.
The 4 tests still FAIL, and are left red on purpose — see #534. Now aimed at
correct-family simulators, the PCCC exchange itself returns BadCommunicationError
for every server-path / client-cip-path permutation, reproduced outside the test
harness via the driver CLI. That is a true signal about the driver (or about
libplctag's PCCC support against ab_server) that skipping would hide. What
changed here is that the failure is now diagnostic instead of an artifact of a
port collision.
Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo
Replace pre-declared options.Tags (typed <Driver>TagDefinition lists) with
options.RawTags (IReadOnlyList<RawTagEntry>) across all 6 Docker-gated driver
integration suites so they COMPILE against the v3 driver API.
Each project gets a local <Driver>RawTags helper mirroring the unit-test
project's helper: it serialises a typed def back to its TagConfig blob via the
driver's <Driver>TagDefinitionFactory.ToTagConfig and packages it as a
RawTagEntry (RawPath = def.Name; DeviceName = def.DeviceHostAddress for the
multi-device drivers AbCip/AbLegacy/TwinCAT/FOCAS). Test intent (addresses,
data types, device routing, assertions) is unchanged — only the
tag-authoring/config-delivery shape was migrated.
Docker-gated: these still skip at runtime without fixtures; COMPILE is the gate.
All five suppressed advisories are now resolved at baseline/resolved versions,
so every NuGetAuditSuppress is removed repo-wide:
- System.Security.Cryptography.Xml (GHSA-37gx-xxp4-5rgx / GHSA-w3x6-4m5h-cxqf)
-> fixed by the .NET 10 baseline (10.0.6)
- OPCFoundation Opc.Ua.Core (GHSA-h958-fxgg-g7w3) -> fixed at resolved 1.5.378.106
Two were still live and are now patched via direct security pins:
- OpenTelemetry.Api 1.9.0 -> 1.15.3 (GHSA-g94r-2vxg-569j) pinned in Cluster;
Runtime/ControlPlane/AdminUI + tests inherit via project reference
- Tmds.DBus.Protocol 0.20.0 -> 0.21.3 (GHSA-xrw6-gwf8-vvr9) pinned in Client.UI
Also correct the Historian sidecar runtime comments (x86 -> x64, matching the
csproj PlatformTarget). Solution audit: 0 vulnerable packages; full build clean.
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public
members surfaced by commentchecker — resolves 5,847 of 5,869 issues
(99.6%) across three /fixdocs passes.
Adds Directory.Packages.props (ManagePackageVersionsCentrally) and
Directory.Build.props (net10.0/nullable/implicit usings/LangVersion latest).
Strips Version attributes from every csproj PackageReference and consolidates
versions into the central file.
Side fixes (necessary to keep the build green on .NET SDK 10.0.105 on macOS):
- Microsoft.CodeAnalysis.CSharp{,.Workspaces}: 5.3.0 -> 5.0.0. The 5.3.0
analyzer DLL references compiler 5.3.0.0 and the local SDK ships compiler
5.0.0.0, producing CS9057 on every project that loaded the Analyzers
output. Master itself was broken on this machine pre-change.
- Server + Server.Tests pin OPCFoundation.NetStandard.Opc.Ua.{Configuration,
Client} to 1.5.374.126 via VersionOverride, matching Opc.Ua.Server's
pin. Mixing 1.5.378.106 Opc.Ua.Core transitively with 1.5.374.126
Opc.Ua.Server breaks CustomNodeManager2 override signatures
(CS0115 on LoadPredefinedNodes/Browse/HistoryRead*) and CS7069 in
the tests. The pin disappears when the legacy Server project is
deleted in Task 56.
- Client.UI + Client.UI.Tests: NuGetAuditSuppress for
GHSA-xrw6-gwf8-vvr9 (Tmds.DBus.Protocol 0.20.0 reaches both projects
transitively from Avalonia.Desktop on Linux/macOS only).
Deviation from the plan: TreatWarningsAsErrors=true is NOT set in
Directory.Build.props because the pre-v2 Admin/Server test projects carry
~240 xUnit1051 analyzer warnings that would fail the build. New v2 projects
opt in via their own csproj; the global flag can return once the legacy
projects are deleted in Task 56.
CLAUDE.md "Docker Workflow" claims (per the 2026-04-28 migration note)
that all fixture-class default endpoints were rewritten to target the
shared Docker host at 10.100.0.35. Audit during today's e2e run showed
the claim was incomplete — five fixture classes still defaulted to
localhost / 127.0.0.1, causing every fixture-touching integration test
to skip with "endpoint unreachable" on a fresh box that hadn't set
the override env vars.
Files corrected:
- tests/.../Modbus.IntegrationTests/ModbusSimulatorFixture.cs
DefaultEndpoint: localhost:5020 → 10.100.0.35:5020
- tests/.../S7.IntegrationTests/Snap7ServerFixture.cs
DefaultEndpoint: localhost:1102 → 10.100.0.35:1102
- tests/.../OpcUaClient.IntegrationTests/OpcPlcFixture.cs
DefaultEndpoint: opc.tcp://localhost:50000 → opc.tcp://10.100.0.35:50000
- tests/.../AbCip.IntegrationTests/AbServerFixture.cs
Host default + ResolveHost fallback: 127.0.0.1 → 10.100.0.35
- tests/.../AbLegacy.IntegrationTests/AbLegacyServerFixture.cs
Host default + ResolveEndpoint fallback: 127.0.0.1 → 10.100.0.35
XML doc comments referencing the old localhost defaults were updated in
the same pass so the class-summary documentation matches the actual
default. The override-via-env-var mechanism (MODBUS_SIM_ENDPOINT,
AB_SERVER_ENDPOINT, AB_LEGACY_ENDPOINT, S7_SIM_ENDPOINT,
OPCUA_SIM_ENDPOINT) is unchanged — pointing at a real PLC or a
locally-running container still works exactly as before.
Verification:
- Solution-wide dotnet build: 0 errors.
- S7.IntegrationTests: 3/3 pass without env-var override.
- OpcUaClient.IntegrationTests: 3/3 pass without env-var override.
- Modbus.IntegrationTests: 4/38 (same as the env-var-override run —
the 2 failures + 32 skips are pre-existing fixture-profile
mismatches unrelated to this fix).
- AbCip.IntegrationTests / AbLegacy.IntegrationTests: same results
as the env-var-override run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrite src/ and tests/ project paths in docs, CLAUDE.md, README.md, and
test-fixture READMEs to the new module-folder layout (Core/Server/Drivers/
Client/Tooling). References to retired v1 projects (Galaxy.Host/Proxy/Shared,
the legacy monolithic test projects) are left untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.
- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
integration, install).
Build green (0 errors); unit tests pass. Docs left for a separate pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>