# 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: - `WorkerHost` does not exist; entry point is `WorkerApplication` (WorkerApplication.cs). - `PipeClient` exists as `WorkerPipeClient` (Ipc/WorkerPipeClient.cs). - `FrameReader`/`FrameWriter` exist as `WorkerFrameReader`/`WorkerFrameWriter` (Ipc/). - `WorkerProtocol` does not exist; closest is `WorkerContractInfo` (Ipc/WorkerContractInfo.cs). - `StaCommandQueue` does not exist; queue logic lives in `StaCommandDispatcher` (Sta/StaCommandDispatcher.cs). - `MessagePump` exists as `StaMessagePump` (Sta/StaMessagePump.cs). - `StaWatchdog` does not exist; watchdog logic lives in `WorkerPipeSession` (Ipc/WorkerPipeSession.cs). - `MxAccessCommandDispatcher` does not exist; actual class is `MxAccessCommandExecutor` (MxAccess/MxAccessCommandExecutor.cs). - `SafeArrayConverter` does not exist; SAFEARRAY conversion is part of `VariantConverter`. - `StatusProxyConverter` does not exist; actual class is `MxStatusProxyConverter` (Conversion/MxStatusProxyConverter.cs). - `HResultMapper` does not exist; actual class is `HResultConverter` (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`, a plain synchronous non-concurrent `Queue` 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` wraps the delegate in a `StaWorkItem`, enqueues it on a `ConcurrentQueue`, and signals `commandWakeEvent`. `StaWorkItem` uses an `Interlocked.CompareExchange` on `started` so exactly one of three outcomes happens. CLAIM_TYPE: behavior-rule VERDICT: accurate EVIDENCE: StaRuntime.cs:12 (`ConcurrentQueue`); 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=`. 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