581 lines
27 KiB
Markdown
581 lines
27 KiB
Markdown
# 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<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
|