Files
mxaccessgw/docs/audit/fragments/02-worker.md
T

581 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (04). 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 (06); 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 1650; keys 58, 59 are absent from 5661. 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 1650, 5661, 541542, 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 06.
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