Files
mxaccessgw/docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md.tasks.json
T

130 lines
44 KiB
JSON

{
"planPath": "docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md",
"tasks": [
{
"id": 0,
"subject": "Task 1: Contract fields and regenerated bindings",
"status": "pending",
"description": "**Goal:** Add the credential/domain fields to the public request and the worker ack commands, plus the worker env-var name, and commit regenerated C# bindings and the client descriptor.\n\n**Files:**\n- Modify: `src/ZB.MOM.WW.MxGateway.Contracts/Protos/mxaccess_gateway.proto` (`AcknowledgeAlarmCommand` ~394-406, `AcknowledgeAlarmByNameCommand` ~415-434, `AcknowledgeAlarmReplyPayload` ~726-737, `AcknowledgeAlarmRequest` ~977-990)\n- Modify: `src/ZB.MOM.WW.MxGateway.Contracts/WorkerLaunchEnvironment.cs`\n- Regenerate: `src/ZB.MOM.WW.MxGateway.Contracts/Generated/MxaccessGateway.cs`, `MxaccessGatewayGrpc.cs`; `clients/proto/descriptors/mxaccessgw-client-v1.protoset`; `clients/rust/protos/mxaccess_gateway.proto` (byte copy)\n- Test: `src/ZB.MOM.WW.MxGateway.Tests/Contracts/ProtobufContractRoundTripTests.cs`\n\n**Acceptance Criteria:**\n- [ ] `AcknowledgeAlarmRequest` has `operator_password = 6` and `operator_domain = 7`; `AcknowledgeAlarmCommand` has `operator_password = 7`; `AcknowledgeAlarmByNameCommand` has `operator_password = 9`; each password field carries the credential comment.\n- [ ] `WorkerLaunchEnvironment.AlarmAckClientName == \"MXGATEWAY_ALARM_ACK_CLIENT_NAME\"`.\n- [ ] Round-trip tests cover the new fields and pass.\n- [ ] `pwsh -File scripts/publish-client-proto-inputs.ps1 -Check` passes and `ClientProtoInputTests` passes.\n- [ ] Rust vendored proto is byte-identical to the Contracts proto.\n\n**Verify:** `dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj --filter \"FullyQualifiedName~ProtobufContractRoundTripTests|FullyQualifiedName~ClientProtoInputTests\"` \u2192 all pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 1: Contract fields and regenerated bindings).\n\n```json:metadata\n{\"files\": [\"src/ZB.MOM.WW.MxGateway.Contracts/Protos/mxaccess_gateway.proto\", \"src/ZB.MOM.WW.MxGateway.Contracts/WorkerLaunchEnvironment.cs\", \"src/ZB.MOM.WW.MxGateway.Contracts/Generated/MxaccessGateway.cs\", \"MxaccessGatewayGrpc.cs\", \"clients/proto/descriptors/mxaccessgw-client-v1.protoset\", \"clients/rust/protos/mxaccess_gateway.proto\", \"src/ZB.MOM.WW.MxGateway.Tests/Contracts/ProtobufContractRoundTripTests.cs\"], \"verifyCommand\": \"dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj --filter \\\"FullyQualifiedName~ProtobufContractRoundTripTests|FullyQualifiedName~ClientProtoInputTests\\\"\", \"acceptanceCriteria\": [\"`AcknowledgeAlarmRequest` has `operator_password = 6` and `operator_domain = 7`; `AcknowledgeAlarmCommand` has `operator_password = 7`; `AcknowledgeAlarmByNameCommand` has `operator_password = 9`; each password field carries the credential comment.\", \"`WorkerLaunchEnvironment.AlarmAckClientName == \\\"MXGATEWAY_ALARM_ACK_CLIENT_NAME\\\"`.\", \"Round-trip tests cover the new fields and pass.\", \"`pwsh -File scripts/publish-client-proto-inputs.ps1 -Check` passes and `ClientProtoInputTests` passes.\", \"Rust vendored proto is byte-identical to the Contracts proto.\"], \"modelTier\": \"standard\"}\n```"
},
{
"id": 1,
"subject": "Task 2: Parse wnwrap ACK_* fields and attribute ack trans...",
"status": "pending",
"description": "**Goal:** Capture the `ACK_COMMENT`/`ACK_OPERATOR_NAME`/`ACK_DOMAIN_NAME`/`ACK_OPERATOR_NODE` elements on snapshot records and use them for Acknowledge transitions and acked snapshot rows.\n\n**Files:**\n- Modify: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAlarmSnapshot.cs`\n- Modify: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/WnWrapAlarmConsumer.cs` (`ParseSnapshotXml` ~739-849)\n- Modify: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs` (`OnTransition` ~235-260, `MapToSnapshot` ~273-274)\n- Test: `src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/WnWrapAlarmConsumerXmlTests.cs`, `AlarmDispatcherTests.cs`\n\n**Acceptance Criteria:**\n- [ ] `ParseSnapshotXml` fills `AckComment`, `AckOperatorName`, `AckOperatorDomain`, `AckOperatorNode` from the live XML sample below.\n- [ ] An `UnackAlm \u2192 AckAlm` transition whose record has `AckOperatorName=\"alice\"`, `AckComment=\"checked\"` enqueues an `OnAlarmTransitionEvent` with `OperatorUser == \"alice\"` and `OperatorComment == \"checked\"`.\n- [ ] Non-ack transitions keep `OperatorUser = OperatorName`, `OperatorComment = AlarmComment`.\n\n**Verify:** `dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter \"FullyQualifiedName~WnWrapAlarmConsumerXmlTests|FullyQualifiedName~AlarmDispatcherTests\"` (windev) \u2192 pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 2: Parse wnwrap ACK_* fields and attribute ack transitions).\n\n```json:metadata\n{\"files\": [\"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAlarmSnapshot.cs\", \"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/WnWrapAlarmConsumer.cs\", \"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs\", \"src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/WnWrapAlarmConsumerXmlTests.cs\", \"AlarmDispatcherTests.cs\"], \"verifyCommand\": \"dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter \\\"FullyQualifiedName~WnWrapAlarmConsumerXmlTests|FullyQualifiedName~AlarmDispatcherTests\\\"\", \"acceptanceCriteria\": [\"`ParseSnapshotXml` fills `AckComment`, `AckOperatorName`, `AckOperatorDomain`, `AckOperatorNode` from the live XML sample below.\", \"An `UnackAlm \\u2192 AckAlm` transition whose record has `AckOperatorName=\\\"alice\\\"`, `AckComment=\\\"checked\\\"` enqueues an `OnAlarmTransitionEvent` with `OperatorUser == \\\"alice\\\"` and `OperatorComment == \\\"checked\\\"`.\", \"Non-ack transitions keep `OperatorUser = OperatorName`, `OperatorComment = AlarmComment`.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 2,
"subject": "Task 3: AlarmAckWriter (authenticated AckMsg write)",
"status": "pending",
"description": "**Goal:** Add the ack value types and `AlarmAckWriter`, which authenticates the operator on its own registered MXAccess server, writes the comment to the ack item, and waits for `OnWriteComplete`.\n\n**Files:**\n- Create: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmAckOperator.cs`, `AlarmAckOutcome.cs`, `AlarmAckTarget.cs`, `AlarmAckResult.cs`, `IAlarmAckWriter.cs`, `AlarmAckWriter.cs`\n- Test: `src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmAckWriterTests.cs`\n\n**Acceptance Criteria:**\n- [ ] With a fake `IMxAccessServer` returning user id 5: `Acknowledge(\"T.A.AckMsg\", \"c\", {alice, CORP, pw})` calls `Register(clientName)` once, `AuthenticateUser(h, \"CORP\\\\alice\", \"pw\")`, `AddItem(\"T.A.AckMsg\")`, `Advise`, `Write(h, item, \"c\", 5)` in that order; a recorded `OnWriteComplete` with category Ok yields `Outcome == Applied`, `NativeStatus == 0`.\n- [ ] Completion with category `SecurityError`, detail 1008 \u2192 `WriteRejected`, `NativeStatus == 1008`, statuses returned.\n- [ ] No completion before the timeout \u2192 `Unconfirmed`, `NativeStatus == 0`.\n- [ ] `AuthenticateUser` throwing `COMException(hr)` or returning `0` \u2192 `AuthenticationFailed`, no write; the password never appears in `Diagnostic` or in the fake's operation log.\n- [ ] A second ack to the same item reuses the item handle (one `AddItem`, one `Advise`).\n- [ ] `AlarmAckOperator.ToString()` does not contain the password.\n\n**Verify:** `dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter \"FullyQualifiedName~AlarmAckWriterTests\"` (windev) \u2192 pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 3: AlarmAckWriter (authenticated AckMsg write)).\n\n```json:metadata\n{\"files\": [\"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmAckOperator.cs\", \"AlarmAckOutcome.cs\", \"AlarmAckTarget.cs\", \"AlarmAckResult.cs\", \"IAlarmAckWriter.cs\", \"AlarmAckWriter.cs\", \"src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmAckWriterTests.cs\"], \"verifyCommand\": \"dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter \\\"FullyQualifiedName~AlarmAckWriterTests\\\"\", \"acceptanceCriteria\": [\"With a fake `IMxAccessServer` returning user id 5: `Acknowledge(\\\"T.A.AckMsg\\\", \\\"c\\\", {alice, CORP, pw})` calls `Register(clientName)` once, `AuthenticateUser(h, \\\"CORP\\\\\\\\alice\\\", \\\"pw\\\")`, `AddItem(\\\"T.A.AckMsg\\\")`, `Advise`, `Write(h, item, \\\"c\\\", 5)` in that order; a recorded `OnWriteComplete` with category Ok yields `Outcome == Applied`, `NativeStatus == 0`.\", \"Completion with category `SecurityError`, detail 1008 \\u2192 `WriteRejected`, `NativeStatus == 1008`, statuses returned.\", \"No completion before the timeout \\u2192 `Unconfirmed`, `NativeStatus == 0`.\", \"`AuthenticateUser` throwing `COMException(hr)` or returning `0` \\u2192 `AuthenticationFailed`, no write; the password never appears in `Diagnostic` or in the fake's operation log.\", \"A second ack to the same item reuses the item handle (one `AddItem`, one `Advise`).\", \"`AlarmAckOperator.ToString()` does not contain the password.\"], \"modelTier\": \"frontier\"}\n```",
"blockedBy": [
0
]
},
{
"id": 3,
"subject": "Task 4: Consumers resolve the ack item instead of acking",
"status": "pending",
"description": "**Goal:** Replace `IMxAccessAlarmConsumer.AcknowledgeByGuid/AcknowledgeByName` with `ResolveAckTarget/ResolveAckTargetByName`, implement them in the wnwrap, subtag and failover consumers, and delete the inert wnwrap ack consumer and the subtag write path.\n\n**Files:**\n- Modify: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IMxAccessAlarmConsumer.cs`, `WnWrapAlarmConsumer.cs`, `SubtagAlarmConsumer.cs`, `FailoverAlarmConsumer.cs`, `ISubtagAlarmSource.cs`, `LmxSubtagAlarmSource.cs`\n- Test: `src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/{WnWrapAlarmConsumerXmlTests,SubtagAlarmConsumerTests,FailoverAlarmConsumerTests,LmxSubtagAlarmSourceTests}.cs` and every test fake implementing the changed interfaces\n\n**Acceptance Criteria:**\n- [ ] `WnWrapAlarmConsumer.ResolveAckTarget(guid)` returns `TestMachine_001.TestAlarm001.AckMsg` for a snapshot record with provider `Galaxy` and that tag; `NotFound` for an unknown GUID; `NotAckable` for provider `InTouch`.\n- [ ] `WnWrapAlarmConsumer.ResolveAckTargetByName(\"TestMachine_001.TestAlarm001\", \"Galaxy\", \"TestArea\")` returns `TestMachine_001.TestAlarm001.AckMsg` without needing a snapshot; non-Galaxy provider \u2192 `NotAckable`.\n- [ ] `SubtagAlarmConsumer` resolves synthetic GUIDs and names to the target's `AckCommentSubtag`; empty subtag \u2192 `NotAckable`; unknown \u2192 `NotFound`.\n- [ ] `FailoverAlarmConsumer` delegates both to `ActiveChild`.\n- [ ] wnwrap no longer creates the second `ackClient` consumer; `ISubtagAlarmSource.Write` and `LmxSubtagAlarmSource.Write` are gone; the solution builds with no warnings.\n\n**Verify:** `dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter \"FullyQualifiedName~WnWrapAlarmConsumerXmlTests|FullyQualifiedName~SubtagAlarmConsumerTests|FullyQualifiedName~FailoverAlarmConsumerTests|FullyQualifiedName~LmxSubtagAlarmSourceTests\"` (windev) \u2192 pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 4: Consumers resolve the ack item instead of acking).\n\n```json:metadata\n{\"files\": [\"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IMxAccessAlarmConsumer.cs\", \"WnWrapAlarmConsumer.cs\", \"SubtagAlarmConsumer.cs\", \"FailoverAlarmConsumer.cs\", \"ISubtagAlarmSource.cs\", \"LmxSubtagAlarmSource.cs\", \"src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/{WnWrapAlarmConsumerXmlTests,SubtagAlarmConsumerTests,FailoverAlarmConsumerTests,LmxSubtagAlarmSourceTests}.cs\"], \"verifyCommand\": \"dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter \\\"FullyQualifiedName~WnWrapAlarmConsumerXmlTests|FullyQualifiedName~SubtagAlarmConsumerTests|FullyQualifiedName~FailoverAlarmConsumerTests|FullyQualifiedName~LmxSubtagAlarmSourceTests\\\"\", \"acceptanceCriteria\": [\"`WnWrapAlarmConsumer.ResolveAckTarget(guid)` returns `TestMachine_001.TestAlarm001.AckMsg` for a snapshot record with provider `Galaxy` and that tag; `NotFound` for an unknown GUID; `NotAckable` for provider `InTouch`.\", \"`WnWrapAlarmConsumer.ResolveAckTargetByName(\\\"TestMachine_001.TestAlarm001\\\", \\\"Galaxy\\\", \\\"TestArea\\\")` returns `TestMachine_001.TestAlarm001.AckMsg` without needing a snapshot; non-Galaxy provider \\u2192 `NotAckable`.\", \"`SubtagAlarmConsumer` resolves synthetic GUIDs and names to the target's `AckCommentSubtag`; empty subtag \\u2192 `NotAckable`; unknown \\u2192 `NotFound`.\", \"`FailoverAlarmConsumer` delegates both to `ActiveChild`.\", \"wnwrap no longer creates the second `ackClient` consumer; `ISubtagAlarmSource.Write` and `LmxSubtagAlarmSource.Write` are gone; the solution builds with no warnings.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 4,
"subject": "Task 5: Route acks through the writer and remove the mana...",
"status": "pending",
"description": "**Goal:** Wire `AlarmAckWriter` through `AlarmCommandHandler` \u2192 `AlarmDispatcher` \u2192 `MxAccessCommandExecutor` with an `AlarmAckOperator` built from the command, map `AlarmAckResult` to the reply, and delete the dead managed/WNAL ack path.\n\n**Files:**\n- Modify: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs`, `AlarmCommandHandler.cs`, `IAlarmCommandHandler.cs`, `MxAccessCommandExecutor.cs` (~894-990), `src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeSession.cs` (:79, :101, :1588), `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessStaSession.cs` (handler factory call ~288)\n- Delete: `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AaManagedAlarmAckConsumer.cs`, `IManagedAlarmAckClient.cs`, `WnalNativeAckConsumer.cs`, `src/ZB.MOM.WW.MxGateway.Worker.Tests/Probes/AlarmManagedGuidAckLiveSmokeTests.cs`\n- Test: `AlarmDispatcherTests.cs`, `AlarmCommandHandlerTests.cs`, `AlarmCommandExecutorTests.cs`, `MxAccessCommandExecutorTests.cs`\n\n**Acceptance Criteria:**\n- [ ] `AlarmDispatcher.Acknowledge(guid, comment, op)` resolves via the consumer and calls the writer with the resolved item; an unresolved target returns `AlarmAckResult.FromTarget` without calling the writer.\n- [ ] `ExecuteAcknowledgeAlarm` builds `AlarmAckOperator{UserName=operator_user, Domain=operator_domain, Password=operator_password}`; maps `Applied`/`Unconfirmed` \u2192 `Ok`, `AlarmNotFound`/`NotAckable` \u2192 `InvalidRequest`, `AuthenticationFailed`/`WriteRejected` \u2192 `MxaccessFailure`; sets `Hresult` and `AcknowledgeAlarm.NativeStatus` to `result.NativeStatus`; copies `result.Statuses` into `reply.Statuses`; `DiagnosticMessage = result.Diagnostic`.\n- [ ] A test with `operator_password = \"pw-secret\"` asserts the secret is absent from `DiagnosticMessage`, `ProtocolStatus.Message`, for every outcome.\n- [ ] No references remain to `AaManagedAlarmAckConsumer`, `IManagedAlarmAckClient`, `WnalNativeAckConsumer`, `MXGATEWAY_USE_MANAGED_ALARM_ACK`, `IsSubtagActive` (`grep -rn` returns nothing under `src/`).\n- [ ] Production wiring creates one `AlarmAckWriter(comFactory, () => new StaMessagePump().PumpPendingMessages(), MxAccessStaSession.ResolveWriteCompletionTimeout(), AlarmAckWriter.ResolveClientName())` per `AlarmCommandHandler`, disposed with the handler.\n- [ ] x86 worker builds with 0 warnings; all `Worker.Tests` pass on windev.\n\n**Verify:** on windev: `powershell -NoProfile -ExecutionPolicy Bypass -File C:\\build\\mxaccessgw-ci\\scripts\\ci\\windev-worker-ci.ps1 -Sha <pushed sha> -Mode test` \u2192 `SUCCESS mode=test`, Failed: 0\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 5: Route acks through the writer and remove the managed path).\n\n```json:metadata\n{\"files\": [\"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs\", \"AlarmCommandHandler.cs\", \"IAlarmCommandHandler.cs\", \"MxAccessCommandExecutor.cs\", \"src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeSession.cs\", \"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessStaSession.cs\", \"src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AaManagedAlarmAckConsumer.cs\", \"IManagedAlarmAckClient.cs\", \"WnalNativeAckConsumer.cs\", \"src/ZB.MOM.WW.MxGateway.Worker.Tests/Probes/AlarmManagedGuidAckLiveSmokeTests.cs\", \"AlarmDispatcherTests.cs\", \"AlarmCommandHandlerTests.cs\", \"AlarmCommandExecutorTests.cs\", \"MxAccessCommandExecutorTests.cs\"], \"verifyCommand\": \"powershell -NoProfile -ExecutionPolicy Bypass -File C:\\\\build\\\\mxaccessgw-ci\\\\scripts\\\\ci\\\\windev-worker-ci.ps1 -Sha <pushed sha> -Mode test\", \"acceptanceCriteria\": [\"`AlarmDispatcher.Acknowledge(guid, comment, op)` resolves via the consumer and calls the writer with the resolved item; an unresolved target returns `AlarmAckResult.FromTarget` without calling the writer.\", \"`ExecuteAcknowledgeAlarm` builds `AlarmAckOperator{UserName=operator_user, Domain=operator_domain, Password=operator_password}`; maps `Applied`/`Unconfirmed` \\u2192 `Ok`, `AlarmNotFound`/`NotAckable` \\u2192 `InvalidRequest`, `AuthenticationFailed`/`WriteRejected` \\u2192 `MxaccessFailure`; sets `Hresult` and `AcknowledgeAlarm.NativeStatus` to `result.NativeStatus`; copies `result.Statuses` into `reply.Statuses`; `DiagnosticMessage = result.Diagnostic`.\", \"A test with `operator_password = \\\"pw-secret\\\"` asserts the secret is absent from `DiagnosticMessage`, `ProtocolStatus.Message`, for every outcome.\", \"No references remain to `AaManagedAlarmAckConsumer`, `IManagedAlarmAckClient`, `WnalNativeAckConsumer`, `MXGATEWAY_USE_MANAGED_ALARM_ACK`, `IsSubtagActive` (`grep -rn` returns nothing under `src/`).\", \"Production wiring creates one `AlarmAckWriter(comFactory, () => new StaMessagePump().PumpPendingMessages(), MxAccessStaSession.ResolveWriteCompletionTimeout(), AlarmAckWriter.ResolveClientName())` per `AlarmCommandHandler`, disposed with the handler.\", \"x86 worker builds with 0 warnings; all `Worker.Tests` pass on windev.\"], \"modelTier\": \"frontier\"}\n```",
"blockedBy": [
1,
2,
3
]
},
{
"id": 5,
"subject": "Task 6: Gateway forwards operator credentials and the ack...",
"status": "pending",
"description": "**Goal:** Forward `operator_password`/`operator_domain` from `AcknowledgeAlarmRequest` onto the worker ack commands, require `operator_user`, treat ack commands as credential-bearing in redaction, and add the `AckClientName` option conveyed to workers.\n\n**Files:**\n- Modify: `src/ZB.MOM.WW.MxGateway.Server/Alarms/GatewayAlarmMonitor.cs` (`BuildAcknowledgeCommand` ~1172-1222)\n- Modify: `src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs` (`AcknowledgeAlarm` ~187-207)\n- Modify: `src/ZB.MOM.WW.MxGateway.Server/Configuration/AlarmsOptions.cs`, `GatewayOptionsValidator.cs` (`ValidateAlarms` ~473-515), `Workers/WorkerProcessLauncher.cs` (~214-224)\n- Modify: `src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactor.cs` (:11-16), `GatewayLogRedactorSeam.cs`\n- Test: `src/ZB.MOM.WW.MxGateway.Tests/Alarms/GatewayAlarmMonitorAcknowledgeTests.cs` (create), `Gateway/Grpc/MxAccessGatewayServiceTests.cs`, `Configuration/GatewayOptionsValidatorTests.cs`, `Gateway/Workers/WorkerProcessLauncherTests.cs`, `Diagnostics/GatewayLogRedactorTests.cs`, `Diagnostics/GatewayLogRedactorSeamTests.cs`\n\n**Acceptance Criteria:**\n- [ ] A GUID-reference ack produces `AcknowledgeAlarmCommand{OperatorUser=\"alice\", OperatorDomain=\"CORP\", OperatorPassword=\"pw-secret\"}`; a `Galaxy!TestArea.T.A` ack produces the by-name command with the same three fields.\n- [ ] The gRPC handler returns `InvalidArgument` \"operator_user is required.\" when `operator_user` is empty.\n- [ ] `GatewayLogRedactor.IsCredentialBearingCommand(\"AcknowledgeAlarm\")` and `(\"AcknowledgeAlarmByName\")` are true; the seam redacts any property whose name contains `password` (case-insensitive).\n- [ ] `MxGateway:Alarms:AckClientName` defaults to `\"MxGateway\"`, rejects values over 64 chars or containing control characters, and the launcher sets `MXGATEWAY_ALARM_ACK_CLIENT_NAME` to it.\n- [ ] Gateway builds with 0 warnings; the filtered tests pass.\n\n**Verify:** `dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj --filter \"FullyQualifiedName~GatewayAlarmMonitorAcknowledgeTests|FullyQualifiedName~MxAccessGatewayServiceTests|FullyQualifiedName~GatewayOptionsValidatorTests|FullyQualifiedName~WorkerProcessLauncherTests|FullyQualifiedName~GatewayLogRedactor\"` \u2192 pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 6: Gateway forwards operator credentials and the ack client name).\n\n```json:metadata\n{\"files\": [\"src/ZB.MOM.WW.MxGateway.Server/Alarms/GatewayAlarmMonitor.cs\", \"src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs\", \"src/ZB.MOM.WW.MxGateway.Server/Configuration/AlarmsOptions.cs\", \"GatewayOptionsValidator.cs\", \"Workers/WorkerProcessLauncher.cs\", \"src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactor.cs\", \"GatewayLogRedactorSeam.cs\", \"src/ZB.MOM.WW.MxGateway.Tests/Alarms/GatewayAlarmMonitorAcknowledgeTests.cs\", \"Gateway/Grpc/MxAccessGatewayServiceTests.cs\", \"Configuration/GatewayOptionsValidatorTests.cs\", \"Gateway/Workers/WorkerProcessLauncherTests.cs\", \"Diagnostics/GatewayLogRedactorTests.cs\", \"Diagnostics/GatewayLogRedactorSeamTests.cs\"], \"verifyCommand\": \"dotnet test src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj --filter \\\"FullyQualifiedName~GatewayAlarmMonitorAcknowledgeTests|FullyQualifiedName~MxAccessGatewayServiceTests|FullyQualifiedName~GatewayOptionsValidatorTests|FullyQualifiedName~WorkerProcessLauncherTests|FullyQualifiedName~GatewayLogRedactor\\\"\", \"acceptanceCriteria\": [\"A GUID-reference ack produces `AcknowledgeAlarmCommand{OperatorUser=\\\"alice\\\", OperatorDomain=\\\"CORP\\\", OperatorPassword=\\\"pw-secret\\\"}`; a `Galaxy!TestArea.T.A` ack produces the by-name command with the same three fields.\", \"The gRPC handler returns `InvalidArgument` \\\"operator_user is required.\\\" when `operator_user` is empty.\", \"`GatewayLogRedactor.IsCredentialBearingCommand(\\\"AcknowledgeAlarm\\\")` and `(\\\"AcknowledgeAlarmByName\\\")` are true; the seam redacts any property whose name contains `password` (case-insensitive).\", \"`MxGateway:Alarms:AckClientName` defaults to `\\\"MxGateway\\\"`, rejects values over 64 chars or containing control characters, and the launcher sets `MXGATEWAY_ALARM_ACK_CLIENT_NAME` to it.\", \"Gateway builds with 0 warnings; the filtered tests pass.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 6,
"subject": "Task 7: .NET client and CLI",
"status": "pending",
"description": "**Goal:** Expose the new fields in the .NET client CLI with safe password input and make failures never echo the password.\n\n**Files:**\n- Modify: `clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClient.cs` (:191-204), `clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayClientCli.cs` (:176-188, :1526-1545, :2468), `clients/dotnet/README.md` (:144-149, :294)\n- Test: `clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientAlarmsTests.cs`, `MxGatewayClientCliTests.cs`\n\n**Acceptance Criteria:**\n- [ ] `AcknowledgeAlarmAsync` doc mentions operator credentials; on failure the thrown exception is passed through `MxGatewaySecretRedaction.Redacted(ex, request.OperatorPassword)`.\n- [ ] CLI `acknowledge-alarm` accepts `--operator`, `--operator-domain`, `--operator-password`, `--operator-password-env` (default env `MXGATEWAY_OPERATOR_PASSWORD`); the request carries them; the password is never printed and is added to the CLI error-redaction list.\n- [ ] Tests assert the request fields and that a failing call's message does not contain the password.\n\n**Verify:** `dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx && dotnet test clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests --filter \"FullyQualifiedName~MxGatewayClientAlarmsTests|FullyQualifiedName~MxGatewayClientCliTests\"` \u2192 pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 7: .NET client and CLI).\n\n```json:metadata\n{\"files\": [\"clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClient.cs\", \"clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayClientCli.cs\", \"clients/dotnet/README.md\", \"clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientAlarmsTests.cs\", \"MxGatewayClientCliTests.cs\"], \"verifyCommand\": \"dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx && dotnet test clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests --filter \\\"FullyQualifiedName~MxGatewayClientAlarmsTests|FullyQualifiedName~MxGatewayClientCliTests\\\"\", \"acceptanceCriteria\": [\"`AcknowledgeAlarmAsync` doc mentions operator credentials; on failure the thrown exception is passed through `MxGatewaySecretRedaction.Redacted(ex, request.OperatorPassword)`.\", \"CLI `acknowledge-alarm` accepts `--operator`, `--operator-domain`, `--operator-password`, `--operator-password-env` (default env `MXGATEWAY_OPERATOR_PASSWORD`); the request carries them; the password is never printed and is added to the CLI error-redaction list.\", \"Tests assert the request fields and that a failing call's message does not contain the password.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 7,
"subject": "Task 8: Python client and CLI",
"status": "pending",
"description": "**Goal:** Regenerate Python bindings and add operator credential options to the CLI with redaction.\n\n**Files:**\n- Regenerate: `clients/python/src/zb_mom_ww_mxgateway/generated/*_pb2*.py` via `clients/python/generate-proto.ps1` (grpcio-tools **1.80.0**)\n- Modify: `clients/python/src/zb_mom_ww_mxgateway/client.py` (:147-161), `src/zb_mom_ww_mxgateway_cli/commands.py` (:527-539, :1156-1166), `clients/python/README.md` (:111-116, :460)\n- Test: `clients/python/tests/test_alarms.py`, `tests/test_review_findings_022_to_026.py`\n\n**Acceptance Criteria:**\n- [ ] Regenerated `_pb2.py` keeps `# Protobuf Python Version: 6.31.1` and only contains the real descriptor delta.\n- [ ] `acknowledge_alarm` redacts `request.operator_password` from raised errors (`redact_secret`).\n- [ ] CLI accepts `--operator-domain`, `--operator-password`, `--operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`); password added to `secrets=`.\n- [ ] README example still parses in `test_review_findings_022_to_026.py`.\n\n**Verify:** `cd clients/python && python -m pytest tests/test_alarms.py tests/test_review_findings_022_to_026.py tests/test_cli.py -q` \u2192 pass\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 8: Python client and CLI).\n\n```json:metadata\n{\"files\": [\"clients/python/src/zb_mom_ww_mxgateway/generated/*_pb2*.py\", \"clients/python/generate-proto.ps1\", \"clients/python/src/zb_mom_ww_mxgateway/client.py\", \"src/zb_mom_ww_mxgateway_cli/commands.py\", \"clients/python/README.md\", \"clients/python/tests/test_alarms.py\", \"tests/test_review_findings_022_to_026.py\"], \"verifyCommand\": \"cd clients/python && python -m pytest tests/test_alarms.py tests/test_review_findings_022_to_026.py tests/test_cli.py -q\", \"acceptanceCriteria\": [\"Regenerated `_pb2.py` keeps `# Protobuf Python Version: 6.31.1` and only contains the real descriptor delta.\", \"`acknowledge_alarm` redacts `request.operator_password` from raised errors (`redact_secret`).\", \"CLI accepts `--operator-domain`, `--operator-password`, `--operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`); password added to `secrets=`.\", \"README example still parses in `test_review_findings_022_to_026.py`.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 8,
"subject": "Task 9: Go client and CLI",
"status": "pending",
"description": "**Goal:** Regenerate Go bindings and add operator credential flags to `mxgw-go acknowledge-alarm` with error redaction.\n\n**Files:**\n- Regenerate: `clients/go/internal/generated/mxaccess_gateway.pb.go`, `mxaccess_gateway_grpc.pb.go` via `clients/go/generate-proto.ps1`\n- Modify: `clients/go/mxgateway/alarms.go` (:15-32), `clients/go/cmd/mxgw-go/main.go` (:1122-1162, usage :1434), `clients/go/README.md` (:141-146, :427)\n- Test: `clients/go/mxgateway/alarms_test.go`\n\n**Acceptance Criteria:**\n- [ ] `AcknowledgeAlarm` wraps errors with `redactSecrets(err, req.GetOperatorPassword())` when the password is non-empty.\n- [ ] CLI flags `-operator-domain`, `-operator-password`, `-operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`) populate the request.\n- [ ] `gofmt -l .` prints nothing; tests pass.\n\n**Verify:** `cd clients/go && gofmt -l . && go build ./... && go test ./...` \u2192 no gofmt output, ok\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 9: Go client and CLI).\n\n```json:metadata\n{\"files\": [\"clients/go/internal/generated/mxaccess_gateway.pb.go\", \"mxaccess_gateway_grpc.pb.go\", \"clients/go/generate-proto.ps1\", \"clients/go/mxgateway/alarms.go\", \"clients/go/cmd/mxgw-go/main.go\", \"clients/go/README.md\", \"clients/go/mxgateway/alarms_test.go\"], \"verifyCommand\": \"cd clients/go && gofmt -l . && go build ./... && go test ./...\", \"acceptanceCriteria\": [\"`AcknowledgeAlarm` wraps errors with `redactSecrets(err, req.GetOperatorPassword())` when the password is non-empty.\", \"CLI flags `-operator-domain`, `-operator-password`, `-operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`) populate the request.\", \"`gofmt -l .` prints nothing; tests pass.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 9,
"subject": "Task 10: Rust client and CLI",
"status": "pending",
"description": "**Goal:** Make the Rust workspace compile against the new fields, add CLI credential options, and keep the password out of `Debug` output and errors.\n\n**Files:**\n- Modify: `clients/rust/src/client.rs` (:335-346), `clients/rust/crates/mxgw-cli/src/main.rs` (:370-381, :1079-1095), `clients/rust/tests/client_behavior.rs` (:219-235, ~:1041-1079, :1423-1440), `clients/rust/README.md` (:81, :123-126), `clients/rust/RustClientDesign.md` (:160, :349)\n- (Vendored proto already updated in Task 1.)\n\n**Acceptance Criteria:**\n- [ ] All `AcknowledgeAlarmRequest { \u2026 }` literals include `operator_password`/`operator_domain` (or `..Default::default()`).\n- [ ] `acknowledge_alarm` attaches the password via `MxAccessError::with_secrets` (or equivalent) so `{:?}` of a returned error lacks it.\n- [ ] CLI `--operator-domain`, `--operator-password`, `--operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`).\n- [ ] Stale README example (`--session-id`, `--alarm-reference`) fixed to `--reference \u2026 --operator \u2026`.\n- [ ] fmt/check/test/clippy clean.\n\n**Verify:** `cd clients/rust && cargo fmt --all --check && cargo check --workspace && cargo test --workspace && cargo clippy --workspace --all-targets -- -D warnings` \u2192 all succeed\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 10: Rust client and CLI).\n\n```json:metadata\n{\"files\": [\"clients/rust/src/client.rs\", \"clients/rust/crates/mxgw-cli/src/main.rs\", \"clients/rust/tests/client_behavior.rs\", \"clients/rust/README.md\", \"clients/rust/RustClientDesign.md\"], \"verifyCommand\": \"cd clients/rust && cargo fmt --all --check && cargo check --workspace && cargo test --workspace && cargo clippy --workspace --all-targets -- -D warnings\", \"acceptanceCriteria\": [\"All `AcknowledgeAlarmRequest { \\u2026 }` literals include `operator_password`/`operator_domain` (or `..Default::default()`).\", \"`acknowledge_alarm` attaches the password via `MxAccessError::with_secrets` (or equivalent) so `{:?}` of a returned error lacks it.\", \"CLI `--operator-domain`, `--operator-password`, `--operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`).\", \"Stale README example (`--session-id`, `--alarm-reference`) fixed to `--reference \\u2026 --operator \\u2026`.\", \"fmt/check/test/clippy clean.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 10,
"subject": "Task 11: Java client and CLI",
"status": "pending",
"description": "**Goal:** Regenerate Java bindings and add operator credential options to the Java CLI with redaction.\n\n**Files:**\n- Regenerate: `clients/java/src/main/generated/main/java/mxaccess_gateway/v1/MxaccessGateway.java` (`gradle :zb-mom-ww-mxgateway-client:generateProto`) \u2014 **commit it** (real proto change)\n- Modify: `clients/java/zb-mom-ww-mxgateway-client/src/main/java/com/zb/mom/ww/mxgateway/client/MxGatewayClient.java` (:280-306), `clients/java/zb-mom-ww-mxgateway-cli/src/main/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCli.java` (:1814-1846), `clients/java/README.md` (:114-118, :420)\n- Test: `clients/java/zb-mom-ww-mxgateway-cli/src/test/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCliTests.java` (:706, :758)\n\n**Acceptance Criteria:**\n- [ ] `acknowledgeAlarm` rethrows failures with `MxGatewaySecrets.redactExact(message, request.getOperatorPassword())` applied when the password is non-empty.\n- [ ] CLI `--operator-domain`, `--operator-password`, `--operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`, picocli `arity`/`interactive` not required).\n- [ ] `readmeDocumentedAcknowledgeAlarmExampleParsesCleanly` updated to the new README tokens and passes; `checkGeneratedClean` passes after commit.\n\n**Verify:** `cd clients/java && JAVA_HOME=/opt/homebrew/opt/openjdk@17 gradle test :zb-mom-ww-mxgateway-client:checkGeneratedClean` \u2192 BUILD SUCCESSFUL\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 11: Java client and CLI).\n\n```json:metadata\n{\"files\": [\"clients/java/src/main/generated/main/java/mxaccess_gateway/v1/MxaccessGateway.java\", \"clients/java/zb-mom-ww-mxgateway-client/src/main/java/com/zb/mom/ww/mxgateway/client/MxGatewayClient.java\", \"clients/java/zb-mom-ww-mxgateway-cli/src/main/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCli.java\", \"clients/java/README.md\", \"clients/java/zb-mom-ww-mxgateway-cli/src/test/java/com/zb/mom/ww/mxgateway/cli/MxGatewayCliTests.java\"], \"verifyCommand\": \"cd clients/java && JAVA_HOME=/opt/homebrew/opt/openjdk@17 gradle test :zb-mom-ww-mxgateway-client:checkGeneratedClean\", \"acceptanceCriteria\": [\"`acknowledgeAlarm` rethrows failures with `MxGatewaySecrets.redactExact(message, request.getOperatorPassword())` applied when the password is non-empty.\", \"CLI `--operator-domain`, `--operator-password`, `--operator-password-env` (default `MXGATEWAY_OPERATOR_PASSWORD`, picocli `arity`/`interactive` not required).\", \"`readmeDocumentedAcknowledgeAlarmExampleParsesCleanly` updated to the new README tokens and passes; `checkGeneratedClean` passes after commit.\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
0
]
},
{
"id": 11,
"subject": "Task 12: Documentation",
"status": "pending",
"description": "**Goal:** Describe the operator-authenticated AckMsg ack everywhere the old alarm-client ack is described, add the credential to the never-log rules, and record the decision.\n\n**Files:**\n- Modify: `gateway.md` (:143-144, :224-237, :1031-1032), `docs/Grpc.md` (:99-101, validation table :219), `docs/Contracts.md` (add ack/credential paragraph near :69-71), `docs/DesignDecisions.md` (:704-713 + new decision section), `docs/AlarmClientDiscovery.md` (\u00a72-\u00a74 :738-843, :997-1019), `docs/AlarmProbeFindings.md` (new \"Fourth attempt (2026-10-19)\" section; update Outcome table), `docs/GatewayConfiguration.md` (:449-455 replace; add `AckClientName` row in the Alarm Options table), `docs/Diagnostics.md` (:7, :66-83), `docs/Authorization.md` (:128 note), `CLAUDE.md` (:129)\n\n**Acceptance Criteria:**\n- [ ] No doc says acks use `AlarmAckByName`/`AlarmAckByGUID` in current tense or mentions `MXGATEWAY_USE_MANAGED_ALARM_ACK` / `AaManagedAlarmAckConsumer` (`grep -rn` over `docs gateway.md CLAUDE.md` returns only historical, dated finding sections).\n- [ ] Grpc.md documents `operator_user` required, `operator_password`/`operator_domain`, the status mapping (Ok / InvalidRequest / MxaccessFailure) and that Historian records the operator; validation table has the `operator_user` row.\n- [ ] GatewayConfiguration.md documents `MxGateway:Alarms:AckClientName` (default `MxGateway`, = Historian OperatorNode).\n- [ ] Diagnostics.md and CLAUDE.md list `AcknowledgeAlarm` `operator_password` among never-log credentials; DesignDecisions.md has \"Alarm acknowledgement writes AckMsg as the authenticated operator\" with the why (alarm-client acks inert headless; AckMsg write historizes the authenticated identity).\n- [ ] AlarmProbeFindings.md's new section records: authenticated AckMsg write acks and logs Operator/Domain/FullName/Node; alarm-client acks inert in every variant (secured and free-access, operator names, consumer names, pump).\n\n**Verify:** `grep -rn \"MXGATEWAY_USE_MANAGED_ALARM_ACK\\|AaManagedAlarmAckConsumer\" docs gateway.md CLAUDE.md` \u2192 only lines inside dated historical sections; manual read of the edited sections.\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 12: Documentation).\n\n```json:metadata\n{\"files\": [\"gateway.md\", \"docs/Grpc.md\", \"docs/Contracts.md\", \"docs/DesignDecisions.md\", \"docs/AlarmClientDiscovery.md\", \"docs/AlarmProbeFindings.md\", \"docs/GatewayConfiguration.md\", \"docs/Diagnostics.md\", \"docs/Authorization.md\", \"CLAUDE.md\"], \"verifyCommand\": \"grep -rn \\\"MXGATEWAY_USE_MANAGED_ALARM_ACK\\\\|AaManagedAlarmAckConsumer\\\" docs gateway.md CLAUDE.md\", \"acceptanceCriteria\": [\"No doc says acks use `AlarmAckByName`/`AlarmAckByGUID` in current tense or mentions `MXGATEWAY_USE_MANAGED_ALARM_ACK` / `AaManagedAlarmAckConsumer` (`grep -rn` over `docs gateway.md CLAUDE.md` returns only historical, dated finding sections).\", \"Grpc.md documents `operator_user` required, `operator_password`/`operator_domain`, the status mapping (Ok / InvalidRequest / MxaccessFailure) and that Historian records the operator; validation table has the `operator_user` row.\", \"GatewayConfiguration.md documents `MxGateway:Alarms:AckClientName` (default `MxGateway`, = Historian OperatorNode).\", \"Diagnostics.md and CLAUDE.md list `AcknowledgeAlarm` `operator_password` among never-log credentials; DesignDecisions.md has \\\"Alarm acknowledgement writes AckMsg as the authenticated operator\\\" with the why (alarm-client acks inert headless; AckMsg write historizes the authenticated identity).\", \"AlarmProbeFindings.md's new section records: authenticated AckMsg write acks and logs Operator/Domain/FullName/Node; alarm-client acks inert in every variant (secured and free-access, operator names, consumer names, pump).\"], \"modelTier\": \"standard\"}\n```",
"blockedBy": [
4,
5
]
},
{
"id": 12,
"subject": "Task 13: Full verification and live Historian check on wi...",
"status": "pending",
"description": "**Goal:** Prove end to end on the windev rig that an `AcknowledgeAlarm` RPC with operator credentials acknowledges the alarm and the Historian events table records that operator.\n\n> **USER-ORDERED GATE \u2014 NON-SKIPPABLE.** This task was requested by the user in the current conversation. It MUST NOT be closed by walking around it, by declaring it \"verified inline\", or by substituting a cheaper check. Close only after every item in `acceptanceCriteria` has been re-validated independently, with output captured.\n\n**Files:**\n- No source changes (deploy only). Uses windev `C:\\build\\mxaccessgw-ci`, `C:\\publish\\mxaccessgw\\Server-YYYYMMDD`, `C:\\publish\\mxaccessgw\\Worker`, NSSM service `MxAccessGw`.\n\n**Acceptance Criteria:**\n- [ ] Gateway full suite passes once on macOS (`dotnet test src/ZB.MOM.WW.MxGateway.Tests`) and windev worker CI `SUCCESS mode=test` with `Failed: 0` for the final SHA.\n- [ ] Server + Worker for the final SHA deployed to windev (Server to a dated `Server-YYYYMMDD` dir with previous `appsettings*.json` copied; Worker swapped with `Worker.bak-YYYYMMDD` backup); `/health/ready` 200; `mxgateway_alarms_provider_mode 1`.\n- [ ] With `TestMachine_001.TestAlarm001` raised (UNACK_ALM), `grpcurl \u2026 AcknowledgeAlarm {\"alarm_full_reference\":\"Galaxy!TestArea.TestMachine_001.TestAlarm001\",\"comment\":\"<unique>\",\"operator_user\":\"Administrator\",\"operator_password\":\"\"}` returns `PROTOCOL_STATUS_CODE_OK`, `hresult` 0.\n- [ ] `mxa read TestMachine_001.TestAlarm001.Acked` \u2192 `True`; `.AckMsg` = the unique comment.\n- [ ] `Runtime.dbo.v_AlarmEventHistory2` newest `ACK_ALM` row for `TestMachine_001.TestAlarm001` has `Operator = Administrator`, `DomainName = ArchestrA`, `OperatorNode = MxGateway`, `Description = <unique comment>`.\n- [ ] Negative: same RPC with `operator_password` = a wrong value for a password-protected account (or `operator_user` = a non-existent user) returns `PROTOCOL_STATUS_CODE_MXACCESS_FAILURE`, the alarm stays unacked, no new ACK_ALM row, and the gateway/worker logs (`Server-*/logs/mxgateway-*.log`, `stderr.log` tail) do not contain the password string.\n- [ ] Rig restored: alarm cleared (`mxa write \u2026 false --secured -u Administrator`), scratch files removed.\n\n**Verify:** windev: `sqlcmd -S localhost -E -W -s \"|\" -i C:\\build\\hist-q.sql` (query below) \u2192 newest ACK_ALM row shows `Administrator|ArchestrA|\u2026|MxGateway|<unique comment>`\n\nFull steps and code: docs/superpowers/plans/2026-10-19-ackmsg-as-operator.md (Task 13: Full verification and live Historian check on windev).\n\n```json:metadata\n{\"files\": [], \"verifyCommand\": \"sqlcmd -S localhost -E -W -s \\\"|\\\" -i C:\\\\build\\\\hist-q.sql\", \"acceptanceCriteria\": [\"Gateway full suite passes once on macOS (`dotnet test src/ZB.MOM.WW.MxGateway.Tests`) and windev worker CI `SUCCESS mode=test` with `Failed: 0` for the final SHA.\", \"Server + Worker for the final SHA deployed to windev (Server to a dated `Server-YYYYMMDD` dir with previous `appsettings*.json` copied; Worker swapped with `Worker.bak-YYYYMMDD` backup); `/health/ready` 200; `mxgateway_alarms_provider_mode 1`.\", \"With `TestMachine_001.TestAlarm001` raised (UNACK_ALM), `grpcurl \\u2026 AcknowledgeAlarm {\\\"alarm_full_reference\\\":\\\"Galaxy!TestArea.TestMachine_001.TestAlarm001\\\",\\\"comment\\\":\\\"<unique>\\\",\\\"operator_user\\\":\\\"Administrator\\\",\\\"operator_password\\\":\\\"\\\"}` returns `PROTOCOL_STATUS_CODE_OK`, `hresult` 0.\", \"`mxa read TestMachine_001.TestAlarm001.Acked` \\u2192 `True`; `.AckMsg` = the unique comment.\", \"`Runtime.dbo.v_AlarmEventHistory2` newest `ACK_ALM` row for `TestMachine_001.TestAlarm001` has `Operator = Administrator`, `DomainName = ArchestrA`, `OperatorNode = MxGateway`, `Description = <unique comment>`.\", \"Negative: same RPC with `operator_password` = a wrong value for a password-protected account (or `operator_user` = a non-existent user) returns `PROTOCOL_STATUS_CODE_MXACCESS_FAILURE`, the alarm stays unacked, no new ACK_ALM row, and the gateway/worker logs (`Server-*/logs/mxgateway-*.log`, `stderr.log` tail) do not contain the password string.\", \"Rig restored: alarm cleared (`mxa write \\u2026 false --secured -u Administrator`), scratch files removed.\"], \"modelTier\": \"frontier\", \"userGate\": true, \"tags\": [\"user-gate\"]}\n```",
"blockedBy": [
4,
5,
6,
7,
8,
9,
10,
11
]
}
],
"lastUpdated": "2026-10-19T20:43:58.922975Z"
}