27 KiB
Cluster 02 — Worker
Auditor: automated prose-documentation audit Docs audited: WorkerBootstrap.md, WorkerConversion.md, WorkerFrameProtocol.md, WorkerProcessLauncher.md, WorkerSta.md, MxAccessWorkerInstanceDesign.md Code verified against: src/ZB.MOM.WW.MxGateway.Worker/**, src/ZB.MOM.WW.MxGateway.Contracts/Protos/mxaccess_worker.proto
DOC: WorkerSta.md
LINES: 23-31
CLAIM: StaRuntime's constructor configures a background Thread named ZB.MOM.WW.MxGateway.Worker.STA and the code snippet shows Name = "ZB.MOM.WW.MxGateway.Worker.STA".
CLAIM_TYPE: term
VERDICT: wrong
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs:61 — actual thread name is "MxGateway.Worker.STA" (no ZB.MOM.WW. prefix).
CODE_AREA: worker.sta
SEVERITY: medium
PROPOSED_FIX: Change every occurrence of ZB.MOM.WW.MxGateway.Worker.STA in WorkerSta.md (prose on line 23 and code snippet on line 29) to MxGateway.Worker.STA.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 254
CLAIM: StaRuntime "starts one background thread named ZB.MOM.WW.MxGateway.Worker.STA".
CLAIM_TYPE: term
VERDICT: wrong
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs:61 — thread is named "MxGateway.Worker.STA".
CODE_AREA: worker.sta
SEVERITY: medium
PROPOSED_FIX: Replace ZB.MOM.WW.MxGateway.Worker.STA with MxGateway.Worker.STA in the STA Runtime section.
DOC: WorkerSta.md
LINES: 144
CLAIM: "InvokeAsync rejects new work with InvalidOperationException" when shutdown is requested.
CLAIM_TYPE: term
VERDICT: wrong
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs:170 — actually throws StaRuntimeShutdownException. That class inherits from InvalidOperationException (StaRuntimeShutdownException.cs:16) but is a distinct type callers are expected to distinguish.
CODE_AREA: worker.sta
SEVERITY: medium
PROPOSED_FIX: Change "rejects new work with InvalidOperationException" to "rejects new work with StaRuntimeShutdownException (a subtype of InvalidOperationException)". The distinction matters because MxAccessStaSession uses it to separate graceful stop from programming errors (e.g., STA-affinity assertions).
DOC: MxAccessWorkerInstanceDesign.md
LINES: 122
CLAIM: Exit code 0 / Success meaning = "Required bootstrap options are valid."
CLAIM_TYPE: behavior-rule
VERDICT: wrong
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerExitCode.cs:5; WorkerBootstrap.md:113 states the authoritative meaning: "The pipe session ran to a clean close." The design-doc description conflates parse success with process-lifetime success.
CODE_AREA: worker.launcher
SEVERITY: high
PROPOSED_FIX: Update the Success row to: "Success | 0 | The pipe session ran to a clean close." Add a note that WorkerBootstrapResult.Succeeded is a parse-phase gate distinct from process exit code 0.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 119-128
CLAIM: Exit code table lists only five codes (0–4). Codes 5 (PipeConnectionFailed) and 6 (ProtocolViolation) are absent.
CLAIM_TYPE: behavior-rule
VERDICT: stale
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerExitCode.cs:5-12 — enum has seven values (0–6); WorkerBootstrap.md:112-120 documents all seven.
CODE_AREA: worker.launcher
SEVERITY: high
PROPOSED_FIX: Add rows for PipeConnectionFailed = 5 ("An IOException or TimeoutException escapes the pipe client") and ProtocolViolation = 6 ("A WorkerFrameProtocolException escapes the pipe client") to the exit-code table.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 134-160
CLAIM: Internal component tree lists class names including WorkerHost, PipeClient, FrameReader, FrameWriter, WorkerProtocol, StaCommandQueue, MessagePump, StaWatchdog, MxAccessCommandDispatcher, SafeArrayConverter, StatusProxyConverter, HResultMapper.
CLAIM_TYPE: term
VERDICT: stale
EVIDENCE: Actual source files in the worker project:
WorkerHostdoes not exist; entry point isWorkerApplication(WorkerApplication.cs).PipeClientexists asWorkerPipeClient(Ipc/WorkerPipeClient.cs).FrameReader/FrameWriterexist asWorkerFrameReader/WorkerFrameWriter(Ipc/).WorkerProtocoldoes not exist; closest isWorkerContractInfo(Ipc/WorkerContractInfo.cs).StaCommandQueuedoes not exist; queue logic lives inStaCommandDispatcher(Sta/StaCommandDispatcher.cs).MessagePumpexists asStaMessagePump(Sta/StaMessagePump.cs).StaWatchdogdoes not exist; watchdog logic lives inWorkerPipeSession(Ipc/WorkerPipeSession.cs).MxAccessCommandDispatcherdoes not exist; actual class isMxAccessCommandExecutor(MxAccess/MxAccessCommandExecutor.cs).SafeArrayConverterdoes not exist; SAFEARRAY conversion is part ofVariantConverter.StatusProxyConverterdoes not exist; actual class isMxStatusProxyConverter(Conversion/MxStatusProxyConverter.cs).HResultMapperdoes not exist; actual class isHResultConverter(Conversion/HResultConverter.cs). CODE_AREA: worker.sta SEVERITY: high PROPOSED_FIX: Rewrite the component tree to match actual class names. This section appears to be a design-phase placeholder that was never updated after implementation.
DOC: WorkerBootstrap.md
LINES: 146
CLAIM: "Standard error is used rather than standard output because the gateway side reads worker stdout for diagnostic capture only, while stderr is reserved for log output that does not interfere with any future stdout-based channel."
CLAIM_TYPE: behavior-rule
VERDICT: stale
EVIDENCE: src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs:166-174 — ProcessStartInfo does not set RedirectStandardOutput = true or RedirectStandardError = true; the gateway currently reads neither stream. The stated reason (gateway reads stdout) is not implemented.
CODE_AREA: worker.launcher
SEVERITY: medium
PROPOSED_FIX: Replace the stdout-capture rationale with the accurate reason: "Environment variables of another process are not visible to other users, unlike command-line arguments; stdout/stderr redirect is not currently wired by the launcher." Alternatively, if stdout capture is a planned feature, label it as such.
DOC: WorkerConversion.md
LINES: 178
CLAIM: "MapCategory and MapSource translate the integer codes documented for MXSTATUS_PROXY (for example 0 = Ok, 3 = CommunicationError, 0 = RequestingLmx, 5 = RespondingAutomationObject)".
CLAIM_TYPE: term
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Conversion/MxStatusProxyConverter.cs:103-133 — MapCategory(0) → MxStatusCategory.Ok; MapCategory(3) → MxStatusCategory.CommunicationError; MapSource(0) → MxStatusSource.RequestingLmx; MapSource(5) → MxStatusSource.RespondingAutomationObject.
CODE_AREA: worker.convert
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerConversion.md LINES: 225 CLAIM: "The mapping covers the engine-error range documented for MXAccess (16-50, 56-61, 541-542, 8017)." CLAIM_TYPE: behavior-rule VERDICT: stale EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Conversion/MxStatusDetailText.cs:7-48 — the dictionary has gaps within those ranges: keys 35, 45, 46 are absent from 16–50; keys 58, 59 are absent from 56–61. The doc implies contiguous ranges. CODE_AREA: worker.convert SEVERITY: low PROPOSED_FIX: Replace the continuous-range description with "selected detail codes in the ranges 16–50, 56–61, 541–542, and 8017 (not all values in those ranges are populated)."
DOC: WorkerBootstrap.md
LINES: 7-8
CLAIM: "WorkerApplication.Run constructs the bootstrap dependencies (EnvironmentVariableWorkerEnvironment, WorkerConsoleLogger writing to Console.Error, and a WorkerPipeClient)".
CLAIM_TYPE: term
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/WorkerApplication.cs:16-19.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerBootstrap.md LINES: 113-120 CLAIM: Exit code table with seven rows 0–6. CLAIM_TYPE: behavior-rule VERDICT: accurate EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerExitCode.cs:5-12. CODE_AREA: worker.launcher SEVERITY: low PROPOSED_FIX: flag only
DOC: WorkerBootstrap.md
LINES: 181-193
CLAIM: WorkerLogRedactor SensitiveFieldNameParts list (seven entries: nonce, secret, password, token, credential, apikey, api_key).
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerLogRedactor.cs:16-25.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerBootstrap.md
LINES: 105
CLAIM: "Succeeded is defined as ExitCode == WorkerExitCode.Success rather than as a separate flag, so the exit code and the success state cannot disagree."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerBootstrapResult.cs:36.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerFrameProtocol.md
LINES: 14-19
CLAIM: Each frame starts with a four-byte little-endian unsigned payload length followed by the serialized WorkerEnvelope payload. Zero-length payloads and payloads larger than the configured maximum are rejected before allocating the payload buffer. The default maximum is 16 MiB.
CLAIM_TYPE: rpc/proto
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameReader.cs:32-50; WorkerFrameProtocolOptions.cs:11 (DefaultMaxMessageBytes = 16 * 1024 * 1024).
CODE_AREA: worker.frameproto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerFrameProtocol.md
LINES: 22-34
CLAIM: Envelope validation checks: protocol_version must match configured version; session_id must match owning session; envelope must contain one typed body value. Violations throw WorkerFrameProtocolException with a WorkerFrameProtocolErrorCode.
CLAIM_TYPE: rpc/proto
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerEnvelopeValidator.cs:16-36.
CODE_AREA: worker.frameproto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerFrameProtocol.md
LINES: 38-41
CLAIM: "The frame protocol lives in ZB.MOM.WW.MxGateway.Worker.Ipc (WorkerFrameReader, WorkerFrameWriter, WorkerFrameProtocolOptions)".
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: Namespaces in WorkerFrameReader.cs:9, WorkerFrameWriter.cs:8, WorkerFrameProtocolOptions.cs:6.
CODE_AREA: worker.frameproto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerFrameProtocol.md
LINES: 44-47
CLAIM: Test file path is src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerFrameProtocolTests.cs.
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: File confirmed at that path.
CODE_AREA: worker.frameproto
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerProcessLauncher.md
LINES: 18-25
CLAIM: Launcher passes SessionId, PipeName, and ProtocolVersion as --session-id, --pipe-name, --protocol-version CLI arguments; nonce travels via MXGATEWAY_WORKER_NONCE environment variable; nonce is excluded from WorkerProcessCommandLine.
CLAIM_TYPE: command
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs:156-184.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerProcessLauncher.md
LINES: 30-34
CLAIM: Launcher validates that the configured worker path exists, has .exe extension, contains a valid Windows Portable Executable header, and matches RequiredArchitecture.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs:189-220 calls WorkerExecutableValidator.Validate.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerProcessLauncher.md
LINES: 35-45
CLAIM: Default probe (IWorkerStartupProbe) "only verifies that the worker did not exit immediately." Retry policy configured by WorkerOptions.StartupProbeRetryAttempts and WorkerOptions.StartupProbeRetryDelayMilliseconds; counter recorded as mxgateway.retries.attempted with area=worker_startup.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: WorkerProcessStartedProbe.cs:10-24 (exits check only); WorkerOptions.cs:18-22; GatewayMetrics.cs:70 (mxgateway.retries.attempted); WorkerProcessLauncher.cs:279 (area label "worker_startup").
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerProcessLauncher.md
LINES: 48-55
CLAIM: Launcher also passes MXGATEWAY_WORKER_PIPE_CONNECT_ATTEMPT_TIMEOUT_MS from WorkerOptions.PipeConnectAttemptTimeoutMilliseconds. On failure, kills the worker process tree, disposes the process handle, disposes the optional pipe reservation, records a worker kill metric, and reports WorkerProcessLaunchException.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: WorkerProcessLauncher.cs:181-182, 253-267.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerProcessLauncher.md
LINES: 60-64
CLAIM: Test command: dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj --filter WorkerProcessLauncherTests.
CLAIM_TYPE: command
VERDICT: accurate
EVIDENCE: Project file confirmed at src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj; test class WorkerProcessLauncherTests confirmed at src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerSta.md
LINES: 14
CLAIM: Type table shows StaCommandDispatcher as "Bounded asynchronous queue in front of StaRuntime…".
CLAIM_TYPE: term
VERDICT: stale
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Sta/StaCommandDispatcher.cs:15 — uses Queue<QueuedStaCommand>, a plain synchronous non-concurrent Queue<T> guarded by lock(gate). There is no async channel or channel-based backpressure; DrainAsync is fire-and-forget but the queue itself is not an async queue.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: Change "Bounded asynchronous queue" to "Bounded queue with an async drain loop" to avoid implying the underlying data structure is an async channel.
DOC: WorkerSta.md
LINES: 56
CLAIM: "The idlePumpInterval defaults to 50 ms so the pump still services Windows messages even when no commands are queued".
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs:30 — TimeSpan.FromMilliseconds(50).
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerSta.md
LINES: 82-99
CLAIM: InvokeAsync<T> wraps the delegate in a StaWorkItem<T>, enqueues it on a ConcurrentQueue<IStaWorkItem>, and signals commandWakeEvent. StaWorkItem<T> uses an Interlocked.CompareExchange on started so exactly one of three outcomes happens.
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: StaRuntime.cs:12 (ConcurrentQueue<IStaWorkItem>); StaRuntime.cs:164-177; StaWorkItem.cs:31,47,57.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerSta.md
LINES: 141-148
CLAIM: Shutdown sequence step 1: sets shutdownRequested under gate; step 2: signals commandWakeEvent; step 3: waits up to timeout on stoppedEvent, which the STA sets after leaving ThreadMain; step 4: drains the queue through CancelQueuedCommands calling CancelBeforeExecution.
CLAIM_TYPE: behavior-rule
VERDICT: stale
EVIDENCE: StaRuntime.cs:261-273 — CancelQueuedCommands() is called inside ThreadMain's finally block before stoppedEvent.Set(), meaning the drain happens on the STA thread, not after stoppedEvent is observed by Shutdown(). Shutdown() calls CancelQueuedCommands() a second time after observing stoppedEvent, but the doc implies a single post-stop drain.
CODE_AREA: worker.sta
SEVERITY: medium
PROPOSED_FIX: Revise step 3 to note that stoppedEvent is set from within ThreadMain's finally block (before the thread exits) after CoUninitialize. Revise step 4 to note the queue is drained twice: once by ThreadMain in its finally (to cancel items enqueued before shutdown) and once by Shutdown() after stoppedEvent (to cancel any items enqueued in the gap).
DOC: WorkerSta.md
LINES: 149
CLAIM: "Dispose calls Shutdown with a five-second budget and only disposes the wait handles when shutdown actually completed".
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: StaRuntime.cs:224-233 — Shutdown(TimeSpan.FromSeconds(5)); handles disposed only when stopped is true.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerSta.md
LINES: 108
CLAIM: "when commandQueue.Count reaches maxPendingCommands (default DefaultMaxPendingCommands = 128) the dispatcher returns a synthetic WorkerUnavailable reply".
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: StaCommandDispatcher.cs:11 (DefaultMaxPendingCommands = 128); lines 125-132 (count check and WorkerUnavailable reply).
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: MxAccessWorkerInstanceDesign.md
LINES: 97
CLAIM: Expected protected environment values include MXGATEWAY_WORKER_LOG_CONTEXT=<optional context>.
CLAIM_TYPE: config-key
VERDICT: wrong
EVIDENCE: No occurrence of MXGATEWAY_WORKER_LOG_CONTEXT anywhere in src/ZB.MOM.WW.MxGateway.Worker/**. The only worker environment variable in code is MXGATEWAY_WORKER_NONCE (WorkerOptions.cs:7) and MXGATEWAY_WORKER_PIPE_CONNECT_ATTEMPT_TIMEOUT_MS (WorkerProcessLauncher.cs:22).
CODE_AREA: worker.launcher
SEVERITY: high
PROPOSED_FIX: Remove MXGATEWAY_WORKER_LOG_CONTEXT from the bootstrap environment table, or add a note that it is not yet implemented if it is intended for a future slice.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 86-99
CLAIM: Bootstrap sequence lists MXGATEWAY_WORKER_LOG_CONTEXT as an optional protected environment value alongside MXGATEWAY_WORKER_NONCE.
CLAIM_TYPE: config-key
VERDICT: wrong
EVIDENCE: Same as above — MXGATEWAY_WORKER_LOG_CONTEXT is not read anywhere in the worker bootstrap code.
CODE_AREA: worker.launcher
SEVERITY: high
PROPOSED_FIX: flag only (same fix as prior entry).
DOC: MxAccessWorkerInstanceDesign.md
LINES: 368-375
CLAIM: "MxAccessEventQueue is the bounded outbound event queue for one worker session. It assigns the monotonic WorkerSequence and WorkerTimestamp when an event is accepted. The default capacity is 10000. When the queue reaches capacity it records a WorkerFaultCategory.QueueOverflow fault and rejects further events."
CLAIM_TYPE: behavior-rule
VERDICT: stale
EVIDENCE: MxAccessEventQueue.cs:115-132 — Enqueue throws MxAccessEventQueueOverflowException in addition to recording the fault. Callers in MxAccessBaseEventSink catch this exception. The doc's phrase "rejects further events" omits the thrown exception, which callers must handle.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: Add that Enqueue raises MxAccessEventQueueOverflowException on overflow, in addition to recording the fault, so that callers know to catch this exception rather than only observing the fault via DrainFault().
DOC: WorkerConversion.md
LINES: 1-262 (entire doc)
CLAIM: Documents VariantConverter, HResultConverter/HResultConversion, MxStatusProxyConverter, MxStatusDetailText, MxStatusConversionException.
CLAIM_TYPE: term
VERDICT: gap
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/Conversion/VariantConverter.cs:129-177 — ConvertToComValue(MxValue) and ConvertToComArray(MxArray) are fully implemented methods that convert protobuf values back to CLR objects for COM write calls. These inverse-projection paths are nowhere mentioned in WorkerConversion.md, leaving integrators unaware of the write path.
CODE_AREA: worker.convert
SEVERITY: medium
PROPOSED_FIX: Add a section "Inverse projection for COM writes" describing ConvertToComValue, its dispatch on MxValue.KindOneofCase, the ConvertToComArray helper, and that raw or unset MxValue payloads throw ArgumentException.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 134-160
CLAIM: Internal component tree for MxAccess subtree lists: MxAccessSession, MxAccessCommandDispatcher, MxAccessEventSink, MxAccessHandleRegistry.
CLAIM_TYPE: term
VERDICT: stale
EVIDENCE: Actual classes: MxAccessSession (internal session state), MxAccessStaSession (owner of the STA session lifecycle), MxAccessCommandExecutor (implements IStaCommandExecutor), MxAccessBaseEventSink/MxAccessAlarmEventSink (event sinks), MxAccessHandleRegistry. The class MxAccessCommandDispatcher does not exist.
CODE_AREA: worker.sta
SEVERITY: medium
PROPOSED_FIX: Update MxAccess subtree to reflect actual class names. Note that MxAccessStaSession owns StaCommandDispatcher (in the Sta namespace) and MxAccessCommandExecutor; they are separate concerns.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 134-160 (entire component tree)
CLAIM: No mention of the alarm subsystem.
CLAIM_TYPE: term
VERDICT: gap
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/MxAccess/ contains a complete alarm subsystem: AlarmCommandHandler.cs, AlarmDispatcher.cs, AlarmRecordTransitionMapper.cs, IAlarmCommandHandler.cs, IMxAccessAlarmConsumer.cs, MxAccessAlarmEventSink.cs, WnWrapAlarmConsumer.cs, MxAlarmSnapshot.cs, MxAlarmStateKind.cs, MxAlarmTransitionEvent.cs. None of these appear in any of the six audited docs. MxAccessStaSession.cs shows an alarmCommandHandlerFactory parameter and an alarm poll loop (lines 14-312).
CODE_AREA: worker.sta
SEVERITY: high
PROPOSED_FIX: Add an "Alarm Subsystem" section to MxAccessWorkerInstanceDesign.md (or create docs/WorkerAlarms.md) covering: IAlarmCommandHandler/AlarmCommandHandler, the WnWrapAlarmConsumer STA-affinity requirement, the 500 ms alarm poll loop in MxAccessStaSession.RunAlarmPollLoopAsync, AlarmDispatcher, and the MxAccessAlarmEventSink. Update the event-sink list in the "Event Sink" section to include alarm events.
DOC: MxAccessWorkerInstanceDesign.md
LINES: 336-338
CLAIM: Event sink must subscribe to OnDataChange, OnWriteComplete, OperationComplete, OnBufferedDataChange.
CLAIM_TYPE: behavior-rule
VERDICT: gap
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessAlarmEventSink.cs exists alongside MxAccessBaseEventSink.cs, indicating a fifth event family (alarm events) is handled. The four-family list is incomplete.
CODE_AREA: worker.sta
SEVERITY: medium
PROPOSED_FIX: Add alarm events to the event sink subscription list and clarify that alarm events are handled via MxAccessAlarmEventSink on the same STA thread.
DOC: WorkerConversion.md
LINES: 17-18
CLAIM: "It accepts an optional expectedDataType so that an MXAccess attribute hint (for example MxDataType.Time for a 64-bit FILETIME) overrides the default CLR-driven projection."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: VariantConverter.cs:262-291 (ConvertInt64Scalar checks expectedDataType == MxDataType.Time && value is long).
CODE_AREA: worker.convert
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerConversion.md
LINES: 112-135
CLAIM: "HResultConverter.Convert prefers COMException.ErrorCode over Exception.HResult because the runtime sometimes overwrites Exception.HResult while marshalling".
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: HResultConverter.cs:21-26.
CODE_AREA: worker.convert
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerBootstrap.md
LINES: 48-54
CLAIM: Three fields arrive on the command line (--session-id, --pipe-name, --protocol-version) and one via environment variable (MXGATEWAY_WORKER_NONCE).
CLAIM_TYPE: command
VERDICT: accurate
EVIDENCE: WorkerOptionsParser.cs:12-14, 78.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerBootstrap.md
LINES: 155-159
CLAIM: "IWorkerLogger exposes only Information and Error. There is no Debug or Trace level."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: IWorkerLogger.cs:8-19.
CODE_AREA: worker.launcher
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerSta.md
LINES: 34
CLAIM: "StaComApartmentInitializer.Initialize calls CoInitializeEx with COINIT_APARTMENTTHREADED (0x2) and treats both S_OK and S_FALSE as success because S_FALSE indicates the apartment was already initialized on this thread."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: StaComApartmentInitializer.cs:8-18.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerSta.md
LINES: 63-78
CLAIM: "StaMessagePump.WaitForWorkOrMessages calls MsgWaitForMultipleObjectsEx with QS_ALLINPUT and MWMO_INPUTAVAILABLE. PumpPendingMessages drains the queue with PM_REMOVE."
CLAIM_TYPE: behavior-rule
VERDICT: accurate
EVIDENCE: StaMessagePump.cs:13-15 (MwmoInputAvailable = 0x0004, PmRemove = 0x0001, QsAllInput = 0x04FF); lines 31-36, 50-57.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: MxAccessWorkerInstanceDesign.md
LINES: 271-286
CLAIM: COM details: interop assembly path, assembly identity (ArchestrA.MxAccess, Version=3.2.0.0, PublicKeyToken=23106a86e706d0ae), COM class ArchestrA.MxAccess.LMXProxyServerClass, CLSID {C30B52F5-2CB5-4760-AF0A-3A344A7EB5DC}, ProgID LMXProxy.LMXProxyServer.1, version-independent ProgID LMXProxy.LMXProxyServer, registered server LmxProxy.dll, threading model Apartment.
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessInteropInfo.cs — ProgId, VersionIndependentProgId, Clsid, InteropAssemblyPath, RegisteredServerPath, ComClassName all match. Assembly identity and threading model are from MXAccess analysis sources and are unverifiable in this repo but consistent with design sources cited in CLAUDE.md.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: MxAccessWorkerInstanceDesign.md
LINES: 656-660
CLAIM: "HeartbeatStuckCeiling (default 75 seconds = 5 × HeartbeatGrace)".
CLAIM_TYPE: config-key
VERDICT: accurate
EVIDENCE: WorkerPipeSessionOptions.cs:19 (DefaultHeartbeatStuckCeiling = TimeSpan.FromSeconds(75)); DefaultHeartbeatGrace = 15 s (line 11); 5 × 15 = 75. ✓
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerBootstrap.md LINES: 5-6 CLAIM: "The worker process is a short-lived child of the gateway." CLAIM_TYPE: term VERDICT: stale EVIDENCE: No functional error, but "short-lived" is context-dependent; workers persist for the entire duration of a gateway session (which may be hours). Integrators might misread this as expecting sub-minute lifetimes. CODE_AREA: worker.launcher SEVERITY: low PROPOSED_FIX: Replace "short-lived child" with "per-session child process" or "child process that lives for the duration of one gateway session."
DOC: MxAccessWorkerInstanceDesign.md
LINES: 151
CLAIM: Component tree lists MxAccessSession as a class under MxAccess.
CLAIM_TYPE: term
VERDICT: accurate
EVIDENCE: src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessSession.cs exists. The tree is incomplete (missing MxAccessStaSession, alarm classes, etc.) but MxAccessSession itself is real.
CODE_AREA: worker.sta
SEVERITY: low
PROPOSED_FIX: flag only (incompleteness covered by the component-tree stale entry above).
DOC: WorkerConversion.md
LINES: 18
CLAIM: VariantConverter is in namespace ZB.MOM.WW.MxGateway.Worker.Conversion.
CLAIM_TYPE: path
VERDICT: accurate
EVIDENCE: VariantConverter.cs:8 (namespace ZB.MOM.WW.MxGateway.Worker.Conversion;).
CODE_AREA: worker.convert
SEVERITY: low
PROPOSED_FIX: flag only
DOC: WorkerFrameProtocol.md
LINES: 49-53
CLAIM: Build command dotnet build src/ZB.MOM.WW.MxGateway.Worker/ZB.MOM.WW.MxGateway.Worker.csproj -p:Platform=x86.
CLAIM_TYPE: command
VERDICT: accurate
EVIDENCE: Project file exists at that path.
CODE_AREA: worker.frameproto
SEVERITY: low
PROPOSED_FIX: flag only