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

27 KiB
Raw Blame History

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