Commit Graph

1231 Commits

Author SHA1 Message Date
Joseph Doherty 200fd6b4c4 docs(audit): FOCAS-Test-Fixture.md — fix duplicated test-file bullet (review fix) 2026-06-03 16:08:15 -04:00
Joseph Doherty 897b06016c docs(audit): OpcUaClient-Test-Fixture.md — accuracy pass
STALE-STATUS (OpcPlcFixture.cs:39):
- "What the fixture is": opc.tcp://localhost:50000 → opc.tcp://10.100.0.35:50000
  (shared Docker host migrated 2026-04-28; fixture already defaults to 10.100.0.35)

CODE-REALITY (OpcUaClientSmokeTests.cs — 3 integration tests open real Secure Channels):
- "What it does NOT cover" §1 ("No UA Secure Channel is ever opened") was wrong
  for the integration suite which does open real channels. Rewritten to scope the
  no-Secure-Channel claim to the unit suite and list what the integration suite
  still doesn't exercise (non-anonymous security policies, signing/encryption,
  chunk assembly, keep-alive).
- "When to trust" table: added Integration (opc-plc) column; noted that real OPC UA
  read + subscribe ARE covered by integration tests; write not yet exercised on wire.

NOTE on IRediscoverable: OpcUaClientDriver does NOT implement IRediscoverable
(verified: no reference in src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/).
Doc makes no such claim — no change needed for that aspect.

INLINE COMPLETENESS:
- "Key fixture / config files": added OpcPlcFixture.cs, OpcUaClientSmokeTests.cs,
  and Docker/docker-compose.yml entries with correct endpoints and flags.
- Added explicit note in OpcUaClientDriver.cs entry: implements IAlarmSource +
  IHistoryProvider (unique among drivers); does NOT implement IRediscoverable.

STRUCTURAL: no rows in links-report.md for this doc.
VERIFY: check_links.py — 0 rows for OpcUaClient-Test-Fixture.md.
2026-06-03 16:02:14 -04:00
Joseph Doherty 2eb3ceb961 docs(audit): S7-Test-Fixture.md — accuracy pass
STALE-STATUS (Snap7ServerFixture.cs:40):
- TL;DR + "What the fixture is": localhost:1102 → 10.100.0.35:1102 (shared
  Docker host migrated 2026-04-28; fixture already defaults to 10.100.0.35)

CODE-REALITY (S7_1500SmokeTests.cs exists and sends real S7comm):
- "What it does NOT cover" §1 ("No ISO-on-TCP frame is ever sent") was
  simply wrong — the integration suite DOES send real S7comm. Rewritten
  to clarify that the unit suite uses IS7Client fakes while the integration
  suite exercises the full wire path.
- "What it does NOT cover" §2 ("successful read not tested end-to-end")
  was also wrong — Driver_reads_seeded_u16_through_real_S7comm does exactly
  that. Rewritten to scope the error-branch-only claim to unit tests.
- "When to trust" table: added Integration (python-snap7) column reflecting
  what the existing S7_1500SmokeTests actually answer.
- "Follow-up candidates" §1: removed the suggestion to build a Snap7 server
  fixture — python-snap7 fixture (task #216) already ships. Follow-ups now
  correctly list Plcsim Advanced and real lab rig only.

INLINE COMPLETENESS:
- "Key fixture / config files": was missing all integration test artefacts.
  Added Snap7ServerFixture.cs, S7_1500SmokeTests.cs, Docker/docker-compose.yml,
  and Docker/profiles/s7_1500.json with descriptions matching file contents.

STRUCTURAL: no broken links in links-report.md for this doc.
VERIFY: check_links.py — 0 rows for S7-Test-Fixture.md.
2026-06-03 16:01:58 -04:00
Joseph Doherty d686e12123 docs(audit): AbLegacy-Test-Fixture.md — accuracy pass
STALE-STATUS: TL;DR claimed "Wire-level round-trip against ab_server PCCC
mode currently fails with BadCommunicationError on read/write (verified
2026-04-20)."  Docker/README.md §Known limitations explicitly states the
root cause was ab_server's empty-CIP-path gate, not a pccc.c gap, and that
N/F/L files round-trip cleanly with the /1,0 path.  AbLegacyReadSmokeTests.cs
confirms tests pass against the fixture.  Rewrote TL;DR + What-the-fixture-is
opening to reflect current passing state; residual gap is only B3 bit-file
writes (0x803D0000).

STALE-STATUS: Lifecycle probe listed as localhost:44818.
AbLegacyServerFixture.cs:57,119 default is 10.100.0.35:44818 (shared Docker
host, migrated 2026-04-28). Fixed.

INLINE COMPLETENESS: Follow-up item 1 phrased as future work ("smoke suite
passes today for N/F/L…"); tightened to describe the current passing state
and narrowed the remaining action to the bit-file write gap.

Verified: python3 .docs-audit/check_links.py — zero rows for this doc.
2026-06-03 16:01:16 -04:00
Joseph Doherty 497d8be1d5 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.
2026-06-03 16:01:06 -04:00
Joseph Doherty 33d40901d2 docs(audit): Modbus-Test-Fixture.md — accuracy pass
STALE-STATUS: TL;DR + Lifecycle section referred to "localhost" as the
simulator address (Modbus-Test-Fixture.md:7,19). Fixture default is
10.100.0.35:5020 (shared Docker host, migrated 2026-04-28) confirmed by
ModbusSimulatorFixture.cs:36.  Updated both prose occurrences.

INLINE COMPLETENESS: Follow-up item 1 claimed MODBUS_SIM_ENDPOINT
lacked documentation; the env var is already documented in this page +
CLAUDE.md. Reworded to reflect actual gap (cross-reference to
test-data-sources.md only).

Verified: python3 .docs-audit/check_links.py — zero rows for this doc.
2026-06-03 16:00:57 -04:00
Joseph Doherty 26833073ca docs(audit): drivers/README.md — capability matrix + link fixes
CODE-REALITY (matrix corrected against driver class declarations):
- Galaxy: GalaxyDriver.cs:38-39 implements IDriver, ITagDiscovery,
  IReadable, IWritable, ISubscribable, IRediscoverable,
  IHostConnectivityProbe, IAlarmSource. Removed the bogus
  IHistoryProvider (no IHistoryProvider refs anywhere in the Galaxy
  project); added the missing IRediscoverable. Replaced the stale
  out-of-process Host/Proxy/named-pipe quirk + the dead
  `Driver.Galaxy.{Shared,Host,Proxy}` path: per CLAUDE.md PR 7.2 those
  retired; the real driver is in-process .NET 10 over gRPC to the
  external mxaccessgw gateway (GalaxyDriver.cs:20-21 doc comment).
  Project path corrected to Driver.Galaxy (+ .Browser, .Contracts).
- Modbus: ModbusDriver.cs:21-22 — added missing IPerCallHostResolver.
- FOCAS: FocasDriver.cs:20-21 — added missing IWritable (it IS
  implemented; WriteAsync returns BadNotWritable for every point,
  FocasDriver.cs:317).
- S7 (S7Driver.cs:31-32), AbCip (AbCipDriver.cs:27-28),
  AbLegacy (AbLegacyDriver.cs:13-14, no IAlarmSource confirmed),
  TwinCAT (TwinCATDriver.cs:13-14), OpcUaClient
  (OpcUaClientDriver.cs:31) verified — already correct.
- Added the 9th family Historian.Wonderware as a server-side historian
  sink (HistorianDataSource.cs:19 `: IHistorianDataSource`), and added
  IHistorianDataSource to the capability-interface list.
- Clarified OpcUaClient as the only driver-side IHistoryProvider; fixed
  the HistoricalDataAccess cross-ref accordingly (the Aveva Historian
  path is the Wonderware IHistorianDataSource sink, not a Galaxy
  IHistoryProvider).
- Added an alarm-source roster to the AlarmTracking cross-ref.

STRUCTURAL (4 dead links repointed to the docs/v1 archive, all verified
to exist):
- ../HistoricalDataAccess.md -> ../v1/HistoricalDataAccess.md (x2)
- ../Subscriptions.md -> ../v1/Subscriptions.md
- Galaxy-Repository.md -> ../v1/drivers/Galaxy-Repository.md
- Galaxy-Test-Fixture.md -> ../v1/drivers/Galaxy-Test-Fixture.md
check_links.py now reports zero rows for docs/drivers/README.md.

STALE-STATUS: removed out-of-process/named-pipe Galaxy wording; noted
native MxAccess alarms work end-to-end; dropped the FOCAS "Tier-C
two-project deployment" phrasing from the per-driver section.
2026-06-03 15:59:57 -04:00
Joseph Doherty c843abf8b1 docs(audit): drivers/FOCAS.md — accuracy pass (no changes needed)
All four dimensions verified against source:

STRUCTURAL: no rows in links-report.md; all 4 linked docs resolve:
  docs/v2/driver-specs.md, docs/v2/focas-version-matrix.md,
  docs/v2/implementation/focas-wire-protocol.md,
  docs/drivers/FOCAS-Test-Fixture.md.

STALE-STATUS: no date anchors, "blocked", "pending", "not yet", "will"
  or TODO phrases found.

CODE-REALITY (verified against src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS/):
  - IAlarmSource implemented at FocasDriver.cs:21
  - IWritable correctly returns BadNotWritable (read-only design)
  - All capability interfaces (IReadable, ITagDiscovery, ISubscribable,
    IHostConnectivityProbe, IPerCallHostResolver) match FocasDriver.cs:21
  - Wire/ directory: WireFocasClient.cs present
  - FocasCapabilityMatrix.cs present

INLINE COMPLETENESS: inventory-diff.md records FOCAS as the only
  fully-covered driver (overview + CLI + fixture) — no gaps to add.
2026-06-03 15:59:31 -04:00
Joseph Doherty 6fa29c6c9a docs(audit): drivers/FOCAS-Test-Fixture.md — accuracy pass
STRUCTURAL
- docs/drivers/FOCAS-Test-Fixture.md line 140: replaced stale
  `Series/FixedTreePopulatesTests.cs` reference (file deleted) with
  `Series/WireBackendTests.cs` — the current home of all fixed-tree
  end-to-end integration tests (verified: ls Series/ shows only
  WireBackendTests.cs + WireBackendCoverageTests.cs).

STALE-STATUS
- Removed `**Status:** as of 2026-04-24` header (date-anchored, stale).
  The architecture description that followed was accurate; the date anchor
  served no purpose once the shim era is closed.

CODE-REALITY
- Line 55: TCP-probe skip gate now mentions `OTOPCUA_FOCAS_SIM_ENDPOINT`
  override (verified in FocasSimFixture.cs line 22 / 49).

FOCAS.md: no changes — all claims verified accurate against source.
- IAlarmSource implemented: FocasDriver.cs:21
- IWritable returns BadNotWritable: FocasDriver.cs (IWritable body)
- All capability interfaces listed in capability table confirmed in
  FocasDriver.cs:21 class declaration
- All linked files exist and resolve correctly
2026-06-03 15:59:14 -04:00
Joseph Doherty d731ed98fa docs(audit): drivers/Galaxy.md — interface list + accuracy pass
CODE-REALITY (known defect): the capability-surface declaration line
omitted IAlarmSource and IAsyncDisposable. GalaxyDriver.cs:39 actually
declares: IDriver, ITagDiscovery, IReadable, IWritable, ISubscribable,
IRediscoverable, IHostConnectivityProbe, IAlarmSource, IDisposable,
IAsyncDisposable. Doc line corrected to match exactly, and an IAlarmSource
row added to the capability table (Runtime/GatewayGalaxyAlarmFeed.cs +
Runtime/GatewayGalaxyAlarmAcknowledger.cs).

STALE-STATUS: the v1-doc move note claimed Galaxy-Repository.md /
Galaxy-Test-Fixture.md 'are being moved to docs/v1/ by a parallel cleanup
track' — that move is complete; they live at docs/v1/drivers/. Rewrote to
present tense and linked the real targets.

Verified against source: deploy-watch is a gRPC stream
(GatewayGalaxyDeployWatchSource forwards WatchDeployEventsAsync via
GalaxyRepositoryClient, not a direct DB poll); contained-name<->tag-name
translation (GalaxyDiscoverer.cs:49,60); DataTypeMap at Browse/DataTypeMap.cs;
IGalaxyHierarchySource / IGalaxyDeployWatchSource / DeployWatcher all present.

check_links.py: zero rows for docs/drivers/Galaxy.md.
2026-06-03 15:58:32 -04:00
Joseph Doherty 33912694fd docs(audit): G1 completeness — driver-lifecycle + alarm-historian reference pages 2026-06-03 15:55:28 -04:00
Joseph Doherty 0a679f2c2a docs(audit): VirtualTags.md — correct write-reject mechanism (review fix) 2026-06-03 15:51:15 -04:00
Joseph Doherty f5552c23d4 docs(audit): ScriptedAlarms.md — accuracy pass
CODE-REALITY (file:line evidence)
- Definition section: removed reference to non-existent
  Phase7EngineComposer.ProjectScriptedAlarms; Phase7Composer is a pure
  data composer (entities → Phase7CompositionResult)
  (src/Server/.../OpcUaServer/Phase7Composer.cs:82-183)
- AlarmSeverity: removed "Phase7EngineComposer.MapSeverity bands it" —
  no such class exists; clarified that AlarmSeverity is defined in
  Core.Abstractions/IAlarmSource.cs not in AlarmTypes.cs
  (src/Core/.../Core.Abstractions/IAlarmSource.cs:87)
- State persistence: replaced "Stream E wires..." planning language with
  actual production class EfAlarmActorStateStore
  (src/Server/.../Runtime/ScriptedAlarms/EfAlarmActorStateStore.cs)
- Composition section: replaced Phase7EngineComposer / Phase7ComposedSources
  references (non-existent) with the actual v2 actor-system composition
  path (ScriptedAlarmEngine + ScriptedAlarmActor + driver-role host startup)
- Key source files: AlarmTypes.cs annotation corrected (adds ShelvingKind,
  names all four state enums, notes AlarmSeverity lives in Core.Abstractions)
- Key source files: Phase7Composer.cs annotation corrected to "pure data
  composer"
- Key source files: ScriptedAlarmActor.cs annotation corrected to describe
  AlarmTransitionEvent + DPS alerts topic (not "OPC UA variable reads")
- Key source files: added EfAlarmActorStateStore as the production
  IAlarmActorStateStore implementation

STALE-STATUS
- "Stream E wires the production implementation" — removed; production
  implementation ships and is named EfAlarmActorStateStore
2026-06-03 15:44:11 -04:00
Joseph Doherty 318e432d93 docs(audit): ReadWriteOperations.md — accuracy pass
STRUCTURAL: Fix broken docs/HistoricalDataAccess.md link → docs/v1/HistoricalDataAccess.md
(file moved to v1/ archive; confirmed present at docs/v1/HistoricalDataAccess.md).

CODE-REALITY: Opening paragraph incorrectly attributed OnReadValue/OnWriteValue hook wiring
to GenericDriverNodeManager. Verified: GenericDriverNodeManager is a plain IDisposable
address-space population helper, not a CustomNodeManager2; it has no read/write hooks
(src/Core/ZB.MOM.WW.OtOpcUa.Core/OpcUa/GenericDriverNodeManager.cs). The v1 DriverNodeManager
that wired those hooks was deleted at 76310b8 (2026-05-26 "chore(cleanup): delete
OtOpcUa.Server, OtOpcUa.Admin, and obsolete v1 tests"). The ADR-002 Phase 7 Stream G
DriverNodeManager replacement is planned but not yet implemented. Current v2 architecture
is a push model: OtOpcUaNodeManager (src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/
OtOpcUaNodeManager.cs) is the CustomNodeManager2; reads return the value last pushed via
WriteValue() from the Akka actor layer. Opening paragraph and Key source files section
updated to reflect present truth; behavioral-contract sections preserved for the ADR-002
planned DriverNodeManager. Flagged in .docs-audit/code-bug-flags.md as CBF-ReadWrite.
2026-06-03 15:44:03 -04:00
Joseph Doherty a32ba1f5c5 docs(audit): VirtualTags.md — accuracy pass
STRUCTURAL
- Repoint Subscriptions.md link → v1/Subscriptions.md (doc line 104;
  target confirmed at docs/v1/Subscriptions.md)

CODE-REALITY (file:line evidence)
- Intro: replace non-existent NodeScopeResolver / DriverNodeManager with
  actual EquipmentNodeWalker + GenericDriverNodeManager; NodeSourceKind
  is stamped by EquipmentNodeWalker.Walk at address-space build time
  (src/Core/.../OpcUa/EquipmentNodeWalker.cs:231,256)
- ScriptSandbox.Build: doc claimed allow-list was "System.Private.CoreLib"
  by name; actual code enumerates TRUSTED_PLATFORM_ASSEMBLIES filtered to
  System.* + netstandard + mscorlib + Microsoft.Win32.Registry
  (src/Core/.../ScriptSandbox.cs:97-127)
- Compile pipeline: doc said "three-step gate"; code has 5 steps —
  EnforceSingleRunMember injection guard (Core.Scripting-013) was missing,
  and PE emit is a distinct step before ALC load
  (src/Core/.../ScriptEvaluator.cs:80-171)
- ForbiddenTypeAnalyzer: doc listed System.Threading.Thread in
  ForbiddenNamespacePrefixes; code explicitly does NOT put it there
  ("Thread's containing namespace is System.Threading, so a prefix check
  never matches") and instead denies it via ForbiddenFullTypeNames; also
  added System.Runtime.Loader and System.Threading.ThreadPool/Timer to
  match the actual deny-list (Core.Scripting-010/-012)
  (src/Core/.../ForbiddenTypeAnalyzer.cs:60-139)
- Dispatch section: DriverNodeManager → GenericDriverNodeManager;
  NodeScopeResolver.IsWriteAllowedBySource (non-existent) removed
- Upstream reads: removed non-existent CachedTagUpstreamSource / Phase7EngineComposer
  references; describe actual DependencyMuxActor → VirtualTagActor feed
- Composition: replaced entire section; Phase7EngineComposer /
  Phase7ComposedSources / PrepareAsync / DriverSubscriptionBridge /
  CachedTagUpstreamSource do not exist in the codebase; Phase7Composer is
  a pure data composer (entities → Phase7CompositionResult)
  (src/Server/.../OpcUaServer/Phase7Composer.cs:82-183)
- Key source files: ScriptEvaluator description updated to "five-step";
  Phase7Composer description corrected; runtime actor descriptions updated

STALE-STATUS
- "Definition reload: handler is not yet wired" — removed (v2 is feature-
  complete; actor-based composition does not use VirtualTagEngine.Load
  as a reload entry point)
2026-06-03 15:42:35 -04:00
Joseph Doherty 9071a3aae0 docs(audit): AddressSpace.md — accuracy + completeness pass
STRUCTURAL: links-report.md has no rows for this doc; check_links.py clean.

STALE-STATUS / CODE-REALITY fixes (file:line evidence):
- 'Galaxy Proxy' / GalaxyProxyDriver.DiscoverAsync retired (PR 7.2) -> GalaxyDriver.DiscoverAsync delegates to GalaxyDiscoverer (Browse/GalaxyDiscoverer.cs:42); removed bogus 'AlarmExtension primitive' + 'two-pass primitive-grouping' claims (IsAlarm comes straight from the gateway hierarchy, GalaxyDiscoverer.cs:71).
- DriverNodeManager.CreateAddressSpace / DriverNodeManager.MapDataType: no such class. Root folder is created by OtOpcUaNodeManager.CreateAddressSpace (OtOpcUaNodeManager.cs:225) as a single shared 'OtOpcUa' root, EventNotifier=None (cs:234-237), not per-driver ns;s={DriverInstanceId}/urn:OtOpcUa:{id}/SubscribeToEvents|HistoryRead. Data-type resolution is OtOpcUaNodeManager.ResolveBuiltInDataType (cs:177) plus per-driver maps (Galaxy Browse/DataTypeMap.Map).
- _securityByFullRef is a Galaxy-driver-internal cache (GalaxyDriver.cs:65/682), not a node-manager field; WriteAuthzPolicy and _writeIdempotentByFullRef do not exist. Rewrote SecurityClass row to the real NodePermissions/TriePermissionEvaluator authz path (TriePermissionEvaluator.cs:78) and WriteIdempotent row to the Polly-retry semantics from DriverAttributeInfo.cs:28-35.
- NodeId scheme table rewritten: string NodeIds under one shared namespace from Config-DB ids / driver refs (Phase7Applier.cs:119-167), not ns;s={DriverInstanceId}.
- Rediscovery: OPC UA Client does NOT implement IRediscoverable (OpcUaClientDriver.cs:31); only Galaxy (DeployWatcher time_of_last_deploy) and TwinCAT (symbol-version-changed 1809) do.
- AB CIP: folder-per-device (AbCipDriver.cs:912-950), not 'per program'; UDT members fan into sub-folders, controller browse into Discovered/.

INLINE COMPLETENESS: added Source (NodeSourceKind) row; documented the two-layer builder->actor->SDK-sink architecture; added EquipmentNodeWalker.cs + Phase7Applier.cs to Key source files.

Verified DataTypeMap.cs lives at the CLAUDE.md-cited path (Driver.Galaxy/Browse/DataTypeMap.cs); contained-name/tag-name + ValueRank/ArrayDim claims cross-checked against Browse/GalaxyDiscoverer.cs:49-71.
2026-06-03 15:42:21 -04:00
Joseph Doherty 2c1dc8bb14 docs(audit): OpcUaServer.md — accuracy + completeness pass
STRUCTURAL: no broken links/paths for this doc (links-report had zero rows);
check_links.py confirms zero rows. All cited src paths verified on disk.

STALE-STATUS (v1->v2):
- Removed v1 'two separate Server/Admin processes' framing; documented the
  single role-gated Host binary + OTOPCUA_ROLES gate
  (src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs; AkkaClusterOptions.cs).
- Server class is OtOpcUaSdkServer (not 'OtOpcUaServer'); it wires ONE
  OtOpcUaNodeManager via CreateMasterNodeManager, not one DriverNodeManager
  per driver. OtOpcUaSdkServer.cs:12-26.
- Removed nonexistent OnServerStarted / LoadServerProperties overrides and
  the 'DriverNodeManagers' member (no such member; grep found none).

CODE-REALITY (doc corrected to match source; no code changed):
- Class name: OtOpcUaSdkServer : StandardServer — OtOpcUaSdkServer.cs:12.
- Address space: OtOpcUaNodeManager : CustomNodeManager2, namespace
  'https://zb.com/otopcua/ns', single 'OtOpcUa' root folder; push-driven via
  IOpcUaAddressSpaceSink — OtOpcUaNodeManager.cs:25,27,225-251.
- Impersonation lives in OpcUaApplicationHost (not the SDK server). Uses
  IOpcUaUserAuthenticator, attaches a UserIdentity (NOT RoleBasedIdentity/
  IRoleBearer — neither exists), Anonymous+X509 fall through to SDK default,
  failures -> BadIdentityTokenRejected (not BadIdentityTokenInvalid).
  OpcUaApplicationHost.cs:159-288.
- Certificate stores default to PkiStoreRoot='pki' (relative to cwd), NOT
  %LOCALAPPDATA%. Substores own/issuer/trusted/rejected.
  AutoAcceptUntrustedClientCertificates default=false (doc had
  Security.AutoAcceptClientCertificates default=true; key does not exist).
  Removed RejectSHA1Certificates claim (not present).
  OpcUaApplicationHost.cs:51,71,298-355.
- Security profiles: EnabledSecurityProfiles default = all three baseline
  profiles, one endpoint per profile; not 'resolved from ServerInstance.Security
  JSON, default None'. Endpoint path is .../OtOpcUa. OpcUaApplicationHost.cs:59-64,321.
- Dispatch: CapabilityInvoker is one per (DriverInstance, IDriver); pipeline
  keyed (DriverInstanceId, hostName, DriverCapability). Enum member is
  'Discover' (not 'Discovery'). Alarm surfaces route via AlarmSurfaceInvoker
  (SubscribeAlarmsAsync/UnsubscribeAlarmsAsync/AcknowledgeAsync), per-host
  fan-out. CapabilityInvoker.cs:7-19,61-156; AlarmSurfaceInvoker.cs:5-51;
  DriverCapability.cs:20-41. OTOPCUA0001 analyzer is category OtOpcUa.Resilience,
  severity Warning — UnwrappedCapabilityCallAnalyzer.cs:67; AnalyzerReleases.Shipped.md:10.
- Authorization: removed nonexistent AuthorizationGate / NodeScopeResolver /
  Authorization:StrictMode / lax-strict mode / WriteAuthzPolicy. Documented the
  real permission-trie infra under Core/Authorization/ (PermissionTrie,
  TriePermissionEvaluator, NodeScope, UserAuthorizationState, AuthorizationDecision).
- Config DB: optimistic concurrency is RowVersion (per-entity), not a
  'DraftRevisionToken' (no such field). sp_PublishGeneration +
  sp_ComputeGenerationDiff verified in Configuration migrations.
- Redundancy: ServiceLevel republished via SdkServiceLevelPublisher
  (IServiceLevelPublisher); ServiceLevelCalculator 0-255. Dropped invented
  'RedundantServerArray' node; standard props are RedundancySupport +
  ServerUriArray. SdkServiceLevelPublisher.cs:9-58; ServiceLevelCalculator.cs:13-23.

INLINE COMPLETENESS: documented EnabledSecurityProfiles binding key in the
Transport section (inventory-diff G3 row owner).
2026-06-03 15:41:38 -04:00
Joseph Doherty 2c938ea6f7 docs(audit): AlarmTracking.md — accuracy + orphan resolution
ORPHAN DECISION: Keep as live doc (path: keep-and-fix).
Rationale: the file carries unique v2 current content describing
the alarms-over-gateway epic architecture; docs/ScriptedAlarms.md
cross-references it explicitly. The orphan symptom is that
docs/README.md still indexes docs/v1/AlarmTracking.md — wiring
this top-level file into README.md is a follow-up task.

STRUCTURAL (dimension 2):
- docs/AlarmTracking.md line 138: Security.md → security.md (CASE-MISMATCH
  from links-report.md rows 1–2). Verified: docs/security.md exists
  (inode 77517627); docs/Security.md is the same file on APFS
  case-insensitive FS, but the checker requires exact on-disk casing.
  check_links.py: zero rows for docs/AlarmTracking.md after fix.

CODE-REALITY (dimension 4):
- line 16 table: `Phase7EngineComposer` / `Phase7EngineComposer.RouteToHistorianAsync`
  → no such class exists. Real class is `Phase7Composer`
  (src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs).
  Scripted-alarm historian routing goes through ScriptedAlarmActor →
  HistorianAdapterActor → IAlarmHistorianSink, not a RouteToHistorianAsync
  method. Fixed to: Phase7Composer / ScriptedAlarmActor transitions →
  HistorianAdapterActor → IAlarmHistorianSink.
- lines 107–123 "Historian write-back" section: referenced
  `Phase7Composer.ResolveHistorianSink` (method doesn't exist in
  current Phase7Composer.cs), `GalaxyProxyDriver` / `GalaxyHistorianWriter`
  (retired in PR 7.2 — no such class in codebase), and `aahClientManaged`
  as a direct call (now mediated through WonderwareHistorianClient).
  Current architecture: NullAlarmHistorianSink default registered in
  ServiceCollectionExtensions.AddOtOpcUaRuntime(); production override
  is SqliteStoreAndForwardSink wrapping WonderwareHistorianClient; bridge
  is HistorianAdapterActor (src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Historian/
  HistorianAdapterActor.cs). Section rewritten to match code reality.
- line 108: "Program.cs" as NullAlarmHistorianSink registration site →
  actual site is ServiceCollectionExtensions.cs, not Program.cs.

STALE-STATUS (dimension 3): no blocked/pending/not-yet banners found
in the top-level file; it was already written as current-state fact.
Galaxy native alarms work end-to-end (verified 2026-05-31) and the
doc correctly describes that as delivered.

CODE-BUG-FLAGS: none. All stale references were doc-side errors; the
production code is correct.

UNVERIFIABLE CLAIMS: AlarmConditionService, DriverNodeManager, ConditionSink,
DriverAlarmSourceAcknowledger, DriverWritableAcknowledger — these are
mentioned by name in the doc but their .cs files were not found in the
search. They may live under a path not searched, or may be internal
implementation details within existing files. These claims are plausible
given the architecture and were not changed.
2026-06-03 15:40:37 -04:00
Joseph Doherty 4cef8124fe chore: ignore .docs-audit scratch dir 2026-06-03 15:24:20 -04:00
Joseph Doherty 4893f7288d docs(plan): documentation audit implementation plan (27 tasks, Phase 0/1/2) 2026-06-03 14:06:24 -04:00
Joseph Doherty 47acdde78d docs(plan): documentation audit design (live-reference corpus, 4 dimensions, fill-every-gap) 2026-06-03 13:59:02 -04:00
Joseph Doherty c6d9b20d9f chore(adminui): prune kit-duplicate + dead shell CSS from site.css
v2-ci / build (push) Failing after 6m40s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
The ZB.MOM.WW.Theme cutover left site.css carrying a near-verbatim copy of the
kit's layout.css (.app-shell/.side-rail/.rail-link/.rail-foot/.login-*) plus two
dead rules (#sidebar-collapse — the kit emits #theme-rail; .rail-eyebrow-chevron
— rendered by the deleted NavSection.razor). Those duplicates loaded after the
kit and could silently override it. Removed them; kept only the app-only rules
the kit does not provide: .rail-eyebrow (footer Session label) and
.chip-alert/.chip-caution (domain status variants). 167 lines removed; builds clean.
2026-06-03 04:37:23 -04:00
Joseph Doherty 11de14d12e refactor(adminui): explicit ClaimTypes.Role footer filter; fix stale NavSidebar comment
v2-ci / build (push) Failing after 45s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
2026-06-03 03:18:08 -04:00
Joseph Doherty aadbf49678 feat(adminui): LoginCard sign-in; remove dead StatusBadge 2026-06-03 03:13:23 -04:00
Joseph Doherty 70d764b063 feat(adminui): MainLayout delegates to ZB.MOM.WW.Theme ThemeShell + kit nav 2026-06-03 03:10:49 -04:00
Joseph Doherty 11bcff6af5 refactor(adminui): drop vendored theme.css/fonts/nav-state.js; keep app-only CSS in site.css 2026-06-03 03:07:21 -04:00
Joseph Doherty de41963587 feat(adminui): use ZB.MOM.WW.Theme ThemeHead + ThemeScripts 2026-06-03 03:03:45 -04:00
Joseph Doherty a78b212c95 build(adminui): reference ZB.MOM.WW.Theme 0.2.0 2026-06-03 03:02:23 -04:00
Joseph Doherty 075c0e69da feat(audit): OtOpcUa IAuditActorAccessor seam + HTTP impl (audit Actor from Auth principal) (Phase 3)
v2-ci / build (push) Failing after 40s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Introduces the IAuditActorAccessor seam and HttpAuditActorAccessor impl so the
ZB.MOM.WW.Audit.AuditEvent Actor field can be sourced from the authenticated Blazor
cookie principal (ZbClaimTypes.Username) when structured emitters are added. Adds the
AuditActor.Resolve static helper (accessor value → SystemFallback/"system") as the
canonical pattern for future emit sites. Wires DI in AddOtOpcUaAuth (TryAddScoped) with
AddHttpContextAccessor(). The structured AuditEvent path remains DORMANT — no live emit
sites exist; seam is forward-looking. SP-based audit path left untouched. 9 new unit
tests all green; Security (54) and ControlPlane (45) test suites fully pass.
2026-06-02 15:25:49 -04:00
Joseph Doherty b7f5e887ee feat(audit): OtOpcUa ConfigAuditLog.Outcome column + migration + ClusterAudit visibility fix (Task 2.2)
Persist the canonical AuditOutcome and make structured audit rows visible.

- ConfigAuditLog gains a nullable Outcome column, stored as the AuditOutcome
  enum member name (nvarchar(16), mirroring how AdminRole is persisted). The
  AuditWriterActor flush now writes Outcome = evt.Outcome.ToString(). Nullable so
  legacy rows and the bespoke stored-procedure path (no derived outcome) write
  NULL.
- Migration 20260602135350_AddConfigAuditLogOutcome: additive nullable column,
  no backfill. Up adds the column, Down drops it. Chains after
  20260602112419_CanonicalizeAdminRoles; `dotnet ef migrations
  has-pending-model-changes` is clean.
- ClusterAudit visibility fix: the page filtered solely on ClusterId, but the
  structured AuditWriterActor path stamps NodeId (ClusterId null), so those rows
  were invisible. Extracted ClusterAuditQuery.ForClusterAsync (shared by the page
  and tests) which ORs in rows whose NodeId belongs to a node in the cluster —
  membership resolved from ClusterNode (NodeId -> ClusterId). SP-path
  ClusterId-stamped rows still match.

Tests: ControlPlane 45/45 (adds Outcome persistence + Denied-outcome asserts);
new Configuration ClusterAuditQueryTests 3/3 (both-paths visible, other-cluster
excluded, page-size cap); AdminUI 121/121. Configuration Unit suite is green on a
clean run (a pre-existing timing flake in ResilientConfigReaderTests, untouched
here, occasionally fails under parallel load and passes in isolation).
2026-06-02 09:59:22 -04:00
Joseph Doherty 933dd1a874 feat(audit): OtOpcUa adopt canonical ZB.MOM.WW.Audit.AuditEvent + AuditWriterActor:IAuditWriter + Outcome derivation (Task 2.1)
Deep-adopt the shared audit record. Deletes the bespoke 8-field positional
Commons AuditEvent and repoints the writer path at ZB.MOM.WW.Audit.AuditEvent
(0.1.0, feed-mapped via dohertj2-gitea). Adds the package reference to both
Commons and ControlPlane.

- AuditWriterActor now implements IAuditWriter: WriteAsync(evt, ct) is a
  best-effort, never-throwing entry point that Self.Tell()s the event onto the
  same batching/dedup/flush pipeline and returns Task.CompletedTask. Existing
  Receive<AuditEvent> + 500/5s batching + two-layer dedup unchanged.
- Flush mapping updated for the canonical field types: OccurredAtUtc is now
  DateTimeOffset (.UtcDateTime into the datetime2 column), SourceNode is string?
  (was NodeId.Value), CorrelationId is Guid? (stored null when null). Outcome is
  NOT yet persisted (column lands in Task 2.2).
- New AuditOutcomeMapper.FromAction maps the OtOpcUa action vocabulary to the
  required canonical Outcome: OpcUaAccessDenied / CrossClusterNamespaceAttempt ->
  Denied; config verbs (DraftCreated/Edited, Published, RolledBack, NodeApplied,
  ClusterCreated, NodeAdded, CredentialAdded/Disabled, ExternalIdReleased) ->
  Success. OtOpcUa emits no Failure events.

The Akka message shape changed, but the structured audit path is dormant (zero
production emit/Tell sites; all live audit flows through the bespoke SP path),
so there is no rolling-deploy wire-compat concern. Tested-not-exercised by
design.

ControlPlane.Tests: 44/44 green (AuditWriterActor suite rewritten to construct
the canonical record + assert the Outcome derivation table + the WriteAsync
best-effort/mailbox-routing contract + null SourceNode/CorrelationId handling).
2026-06-02 09:53:12 -04:00
Joseph Doherty c1619d95f5 feat(auth)!: OtOpcUa canonical control-plane roles + config-DB migration (Task 1.7)
Standardize the control-plane admin role VALUES on the canonical six
(ZB.MOM.WW.Auth CanonicalRole). OtOpcUa uses four:
  ConfigViewer   -> Viewer
  ConfigEditor   -> Designer
  FleetAdmin     -> Administrator
  DriverOperator -> Operator   (appsettings-only string role)

This is a rename, not a permission change: enforcement semantics are
preserved (whoever could deploy/administer/operate before still can).

- AdminRole enum members renamed (persisted as string names via
  HasConversion<string>); RoleGrants.razor dropdown default updated.
- EF DATA migration CanonicalizeAdminRoles rewrites existing
  LdapGroupRoleMapping.Role rows old->new (Up) and back (Down); schema /
  model snapshot byte-identical (no pending model changes).
- Enforcement role STRINGS canonicalized:
  * Security policies keep their NAMES ("DriverOperator"/"FleetAdmin")
    but require canonical roles: RequireRole("Operator","Administrator")
    and RequireRole("Administrator").
  * Deployments.razor [Authorize(Roles="Administrator,Designer")].
  * DevStub now grants "Administrator"; LdapOptions/doc-comment examples
    canonicalized.
- Data-plane authorization (NodePermissions/NodeAcl/IPermissionEvaluator/
  TriePermissionEvaluator/UserAuthorizationState) UNTOUCHED.
- New CanonicalAdminRolesTests pins canonical claim values end-to-end and
  the real registered policies; existing role-string tests updated.
2026-06-02 07:30:00 -04:00
Joseph Doherty 8ba289f975 chore(auth): OtOpcUa unify dev LDAP base DN to dc=zb,dc=local (Task 1.6)
Replace all dev-directory dc=lmxopcua,dc=local references with dc=zb,dc=local
across LdapOptions default, integration harness overrides, docker-compose LDAP_ROOT,
AclEdit placeholder DN, and dev/smoke-test docs. CN/OU prefixes preserved.
2026-06-02 06:45:23 -04:00
Joseph Doherty d0777eee29 fix(auth): OtOpcUa Task 1.5 review — pin JWT role-claim test + document issued-only JWT role key
Fix 1 (test): Token_payload_uses_canonical_zb_claim_keys now asserts that the JWT
payload carries at least one role under JwtTokenService.RoleClaimType ("Role"),
pinning the role-key contract so a future rename is caught immediately. Adds a
comment explaining why alice has roles (appsettings "ReadOnly"→"ConfigViewer"
baseline). Adds missing `using ZB.MOM.WW.OtOpcUa.Security.Jwt` to the test file.

Fix 2 (no-validation path — no AddJwtBearer in production pipeline): grep of src/
confirms no AddJwtBearer / JwtBearer scheme in ServiceCollectionExtensions or Host;
the ServiceCollectionExtensions doc comment explicitly states "no JwtBearer parallel
scheme". RoleClaimType intentionally stays the short "Role" key. Three changes:
  - RoleClaimType doc comment documents issued-only nature, the caveat that a
    JwtBearer scheme MUST use BuildValidationParameters(), and that BuildValidationParameters
    is already wired to set RoleClaimType+NameClaimType correctly.
  - Issue() inline comment at the role-mint site references RoleClaimType docs.
  - BuildValidationParameters() now sets RoleClaimType=RoleClaimType and
    NameClaimType=UsernameClaimType so that if it is ever passed to AddJwtBearer,
    role/name resolution is correct without any extra wiring. TryValidate() is
    refactored to delegate to BuildValidationParameters() so the two can never drift.

All 35 security tests green.
2026-06-02 06:30:10 -04:00
Joseph Doherty 83856b7c27 feat(auth): OtOpcUa adopt ZbClaimTypes + ZbCookieDefaults, keep cookie name (Task 1.5)
Add ZB.MOM.WW.Auth.AspNetCore package ref to Security project (version 0.1.1
from central PM). Alias JwtTokenService.UsernameClaimType and DisplayNameClaimType
to ZbClaimTypes.Username ("zb:username") and ZbClaimTypes.DisplayName ("zb:displayname")
so every mint/read site inherits the canonical spelling. AuthEndpoints login path now
emits ZbClaimTypes.Name (= ClaimTypes.Name, populates Identity.Name) instead of
ClaimTypes.NameIdentifier (no other read site used it), and references ZbClaimTypes.Role
(= ClaimTypes.Role) for role claims so [Authorize(Roles=...)] continues to resolve.
Cookie hardening now flows through ZbCookieDefaults.Apply (sets HttpOnly, SameSite=Strict,
SlidingExpiration, SecurePolicy, ExpireTimeSpan) followed by opts.Cookie.Name = v.Name to
preserve the OtOpcUa-specific "ZB.MOM.WW.OtOpcUa.Auth" cookie name. Two new tests added
to AuthEndpointsIntegrationTests assert canonical ZbClaimTypes on the cookie principal and
canonical zb: keys in the JWT payload; all 35 security tests green.
2026-06-02 06:11:00 -04:00
Joseph Doherty c4f315ec90 fix(auth): OtOpcUa 1.2 review fixes — startup insecure-transport guard + Ldaps in prod overlays, test fidelity, 0.1.1 pin 2026-06-02 01:37:29 -04:00
Joseph Doherty 257caa7bd1 feat(auth): cut OtOpcUa over to ZB.MOM.WW.Auth.Ldap; preserve DevStubMode; route roles via IGroupRoleMapper (Task 1.2/1.4) 2026-06-02 00:55:10 -04:00
Joseph Doherty 6534875476 feat(auth): add IGroupRoleMapper<string> seam (Task 1.1) 2026-06-02 00:29:45 -04:00
Joseph Doherty d2d7730830 build: add ZB.MOM.WW.Auth/Audit feed mapping + version pins
Maps ZB.MOM.WW.Auth, ZB.MOM.WW.Auth.*, ZB.MOM.WW.Audit to the gitea feed
and pins Auth.Abstractions/Ldap/AspNetCore + Audit at 0.1.0. No project
references yet (added during Phase 1/2 adoption). OtOpcUa omits Auth.ApiKeys
(OPC UA transport security).
2026-06-02 00:16:39 -04:00
Joseph Doherty 2844180865 fix: honor LdapOptions.Enabled at runtime; dedupe ILdapAuthService registration; +SearchBase test, doc fix
v2-ci / build (push) Failing after 41s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
2026-06-01 23:03:12 -04:00
Joseph Doherty d3ab2bfbaf fix: bind OtOpcUa LdapOptions from real Security:Ldap section; gate validator on DevStubMode 2026-06-01 22:46:09 -04:00
Joseph Doherty 88e773af36 feat: validate OpcUa host options at startup (route through IOptions + ValidateOnStart) 2026-06-01 18:45:55 -04:00
Joseph Doherty f35ebd7aaf feat: add fail-fast LDAP options validation in OtOpcUa via ZB.MOM.WW.Configuration 2026-06-01 18:32:44 -04:00
Joseph Doherty 0cbb82e466 build: add ZB.MOM.WW.Configuration feed mapping + version pin 2026-06-01 18:10:28 -04:00
Joseph Doherty 7b6884031d Merge feat/telemetry-followons: telemetry follow-ons for OtOpcUa
v2-ci / build (push) Failing after 34s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Serilog.AspNetCore/Extensions.Hosting/Settings.Configuration aligned to 10.0.0;
config-driven OTLP exporter opt-in (default Prometheus; also makes recorded
spans exportable when OTLP is configured).
2026-06-01 17:17:23 -04:00
Joseph Doherty 7ff7a60ae0 feat(otopcua): config-driven OTLP exporter opt-in (default Prometheus) 2026-06-01 16:40:24 -04:00
Joseph Doherty 8faa2bf23d build(otopcua): align Serilog.AspNetCore/Extensions.Hosting/Settings.Configuration to 10.0.0 2026-06-01 16:35:34 -04:00
Joseph Doherty 2099713ed8 Merge feat/adopt-zb-telemetry: adopt ZB.MOM.WW.Telemetry across OtOpcUa
v2-ci / build (push) Failing after 51s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
AddZbTelemetry (shared OTel Resource identity + standard instrumentation;
kept meter ZB.MOM.WW.OtOpcUa + /metrics) and AddZbSerilog (shared enrichers +
trace correlation; sinks moved to appsettings). Behaviour-preserving.
2026-06-01 16:05:34 -04:00
Joseph Doherty c05ffc7b39 build(otopcua): add <clear/> to NuGet.config packageSources for supply-chain hygiene parity 2026-06-01 16:03:15 -04:00
Joseph Doherty 60017177cb feat(otopcua): adopt AddZbSerilog (shared enrichers + trace correlation); sinks to config 2026-06-01 15:41:21 -04:00