Converges all five clients on one version after four had drifted onto the
already-published 0.1.2/0.1.1 while their APIs kept changing underneath it:
- Rust Cargo.toml [package] + [workspace.package] -> 0.2.0 (CLIENT_VERSION
already derives from CARGO_PKG_VERSION, no separate edit).
- Python pyproject.toml + version.py -> 0.2.0; new test asserts __version__
matches pyproject.toml (closes the CLI-26 residual drift mode).
- Go mxgateway/version.go ClientVersion -> 0.2.0.
- .NET ZB.MOM.WW.MxGateway.Client.csproj <Version> -> 0.2.0.
- Java -> 0.2.1, not 0.2.0: the live Gitea Maven feed already had 0.2.0
published (2026-06-26), before the CLI-37/38/40/41 conformance fixes
changed the client's observable behavior, so reusing 0.2.0 would label
two different APIs identically. Recorded as an exception in
docs/ClientPackaging.md's new Versioning section.
Publish-pipeline guards:
- scripts/tag-go-module.ps1 implements the CLI-21 guard: after semver
validation it refuses to tag unless clients/go/mxgateway/version.go's
ClientVersion already matches the requested tag version.
- scripts/pack-clients.ps1 gains a Gitea package-registry collision guard
wired into every per-language -Publish step; it aborts if the target
name+version already exists rather than force-overwriting. Verified live
against the real Gitea registry (credentials already present in this
environment) — correctly refuses on every known-published artifact and
passes on every unpublished target.
Docs updated in the same commit: docs/ClientPackaging.md (new Versioning
section), and the five client READMEs' stale 0.1.1/0.1.2 example versions.
No .proto changes. No publish performed.
All five client CLIs now share one credential contract for `authenticate-user`:
flags `--password` / `--password-env` (Go: `-password` / `-password-env`) with
default env `MXGATEWAY_VERIFY_PASSWORD`, resolution flag-then-env, and a resolved
credential that is missing *or empty* is a usage error naming the flag and the
variable. The value is never echoed and never reaches the wire.
Go and Java previously sent an empty credential when the variable was unset,
turning a misconfigured environment into a real MXAccess authentication attempt.
Go now returns the guard error before dialing; Java throws a picocli
ParameterException instead of falling back to "". Python's `--password-env`
gained the canonical default and its UsageError names the resolved variable.
Rust treats an empty flag or env value as missing, with the resolution extracted
into a testable `resolve_verify_user_password`. .NET adopts the canonical flags
and keeps `--verify-user-password`, `--verify-user-password-env`, and
MXGATEWAY_VERIFY_USER_PASSWORD as deprecated aliases for one release.
Docs same commit: CrossLanguageSmokeMatrix.md gains the credential contract and
the per-CLI subcommand-coverage table (the documented-not-fixed half of the
finding); all five READMEs name the canonical variable and the fail-fast rule,
and the .NET README carries the deprecation note. Tracking flipped to Done in
both remediation registers with a change-log row.
No .proto changed; no generated code regenerated.
One cross-client conformance pass; also closes first-cycle CLI-08.
CLI-37: an MxStatusProxy entry is a failure iff `category !=
MX_STATUS_CATEGORY_OK`. The proto contract has always said so — `success` is
the raw 16-bit COM member carried verbatim for diagnostics, not a boolean — but
four clients branched on `success` alone and .NET required both, so the same
gateway reply produced opposite verdicts per language. An absent entry stays
success; a present entry with an UNSPECIFIED category is a failure, because the
worker always maps a category and an unmapped one is not proven OK.
CLI-38: a reply fails on HRESULT iff `hresult` is present and negative, so
positive COM success codes such as S_FALSE (1) pass. .NET/Go/Java used `!= 0`,
which errored on a parity-preserving S_FALSE that Python and Rust accepted.
This makes the existing ClientLibrariesDesign.md claim true rather than
rewriting the doc to describe the divergence.
Four shared fixtures pin both rules cross-client, and each language suite also
carries a table test for the two edges a fixture cannot express (absent entry,
UNSPECIFIED category). A Java test fake that built a status with a bare
`setSuccess(1)` and no category is fixed — under the category rule that reply
was never a success.
Completes both findings across all five clients — done locally on the Mac now
that homebrew openjdk@17 is available (JAVA_HOME=/opt/homebrew/opt/openjdk@17).
CLI-15: new MxEventStreamItem record + MxEventStream.nextItem() surfaces the
gateway's ReplayGap sentinel as a typed, non-terminal signal (isReplayGap()/
replayGap()/event()); existing Iterator<MxEvent> path unchanged, sentinel never
swallowed/synthesized. Javadoc covers gap semantics + resume contract.
CLI-04: typed single-item helpers on MxGatewaySession — Phase 1
adviseSupervisory/writeSecured/writeSecured2/authenticateUser/archestrAUserToId,
Phase 2 addBufferedItem/setBufferedUpdateInterval/suspend/activate (unregister
already present). Each routes through invokeCommand -> ensureProtocolSuccess +
ensureMxAccessSuccess (same validation as bulk). MXAccess parity preserved.
Credentials flow only into the request proto; exceptions carry only the reply and
gRPC status text is scrubbed via MxGatewaySecrets.redactCredentials — tests
assert the password/secured value is absent from getMessage()/toString()/CLI
output. New CLI subcommands write-secured/authenticate-user (credential via
--password/--password-env, prints only the user id).
gradle test: 106 tests, 0 failures (58 client + 48 cli); no generated churn.
Shared docs: ClientLibrariesDesign + CLAUDE.md updated to "all five clients".
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Reconcile load-bearing docs with shipped behavior:
- IPC-06: gateway.md Worker Envelope sketch -> points to mxaccess_worker.proto
as source of truth (string correlation_id, real oneof arms incl.
worker_shutdown_ack/worker_ready).
- IPC-07: docs/Grpc.md six RPCs -> seven; document QueryActiveAlarms handler
+ validation row.
- IPC-21: gateway.md Session RPC moved from live API into a 'Future work: not
implemented' subsection.
- TST-13: drop stale design-era sketches from gateway.md; correct the
single-subscriber-default (config-gated fan-out) note.
- SEC-09: dashboard GroupToRole sample GwAdmin:Admin -> Administrator so it
passes GatewayOptionsValidator; clarify Administrator is the canonical role.
- SEC-22: rewrite docs/Authentication.md to the pipeline that actually ships
(ZB.MOM.WW.Auth.ApiKeys package + gateway-owned CachingApiKeyVerifier,
CoalescingMarkApiKeyStore, CanonicalForwardingApiKeyAuditStore, etc.);
remove 18 stale type names (grep-verified absent).
- IPC-17: correct wrong Python generated dir (mxgateway -> zb_mom_ww_mxgateway)
in CLAUDE.md + 3 docs.
- CLI-12: Java docs Java 21 -> Java 17 (JDK17 retarget for Ignition 8.3).
- CLI-16: docs/ClientPackaging.md reconciled with real .slnx, Python package
name, and gradle project names; fix stale generateProto task name.
Docs-only; type/path/version claims verified against source.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
A consuming project hit two MXAccess parity surprises: a plain Write only
records its user_id when the item has an active supervisory advise (the path
to take when not authenticating), and array writes replace the whole array
rather than patching individual elements. Document both across the five client
READMEs and gateway.md's compatibility baseline, and expose the missing
advise-supervisory subcommand in the go/python/rust/java CLIs (plus the .NET
help text) so callers can establish the supervisory advise without dropping to
the raw command API.
- D4: add 'ping' subcommand (MX_COMMAND_KIND_PING / PingCommand{message}),
accepting --session-id and optional --message (default "ping"); prints the
worker's echoed diagnostic message.
- D8-java: add 'galaxy-browse' subcommand over browse()/LazyBrowseNode.expand()
and raw BrowseChildren paging for --parent. JSON node shape matches the
cross-client surface (flattened object fields + hasChildrenHint + nested
children array).
- D9-java: make galaxy-test-connection / galaxy-last-deploy the primary names,
keeping galaxy-test / galaxy-deploy-time as deprecated picocli aliases.
- Tests for ping, galaxy-browse JSON hasChildrenHint key, and alias resolution.
- README updated for the new/renamed subcommands.
Add a "High-level walker" subsection under each client's "Browsing
lazily" section showing idiomatic use of LazyBrowseNode (browse +
expand, idempotency note, redeploy refresh pattern).
Client.Java-032 README CLI examples for stream-alarms and
acknowledge-alarm now use the correct picocli flags
(--filter-prefix and --reference); two regression
tests parse each documented invocation.
Client.Java-033 StreamAlarmsCommand publishes an
AtomicReference<MxGatewayAlarmFeedSubscription> and
mirrors MxEventStream's overflow branch: a failed
queue.offer cancels the subscription, queues an
IllegalStateException, then queues the END sentinel
— preserving the fail-fast contract.
Client.Java-034 BatchCommand routes through a new
MxGatewayCli.tokenizeBatchLine POSIX-style shell
tokenizer that respects double-quoted, single-quoted,
and backslash-escaped arguments.
Client.Java-035 Added streamAlarmsForwardsRequestAndStreamsAlarmFeedMessages
to MxGatewayClientSessionTests; asserts request shape,
message ordering, and cancellation propagation.
Client.Java-036 Extracted MxGatewayStreamSubscription<TRequest,TResponse>
abstract base; the four subscription classes
(MxGatewayEventSubscription, MxGatewayAlarmFeedSubscription,
MxGatewayActiveAlarmsSubscription, DeployEventSubscription)
collapse to ~10-line subclasses. A new contract test
runs identical lifecycle / cancellation assertions
across all four subclasses.
All resolved at 2026-05-24; gradle build + gradle test BUILD SUCCESSFUL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each client's README now covers the alarms surface in both the SDK
section (StreamAlarms / AcknowledgeAlarm beside the existing
QueryActiveAlarms entry, with the streaming-cancellation note) and
the CLI examples (stream-alarms / acknowledge-alarm invocations
mirroring the in-tree implementations across .NET, Go, Rust, Python,
and Java).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Client.Java-027 (Documentation): Updated 17 Gradle task references in
clients/java/README.md (lines 37, 108-110, 160-161, 169-176, 186, 206,
221) and 3 in clients/java/JavaClientDesign.md from the retired short
subproject names to the canonical zb-mom-ww-mxgateway-client /
zb-mom-ww-mxgateway-cli names. Copy-pasting any documented command now
matches the subproject names declared in settings.gradle.
Client.Java-028 (Design adherence): Build-layout block in
JavaClientDesign.md lines 23-27 updated to show the actual package
paths com/zb/mom/ww/mxgateway/{client,cli}/ instead of the retired
com/dohertylan/mxgateway/{client,cli}/ paths.
Client.Java-029 (Documentation): README.md line 210 corrected from
"zb-mom-ww-mxgateway-cli/build/install/mxgateway-cli" to
"zb-mom-ww-mxgateway-cli/build/install/zb-mom-ww-mxgateway-cli" — Gradle
installDist produces a directory whose name matches the project name,
not the short suffix. The e2e script already used the correct path.
Client.Java-030 (Testing coverage): Added
queryActiveAlarmsForwardsRequestAndStreamsSnapshots to
MxGatewayClientSessionTests. The test pushes a QueryActiveAlarmsRequest
carrying session_id / client_correlation_id / alarm_filter_prefix
through an InProcessGateway + TestGatewayService and asserts the server
observed all three request fields, two ActiveAlarmSnapshots stream in
order, and onError is never called. TDD red→green confirmed via a
deliberately-wrong session_id assertion. The re-triage note in
Client.Java-030's resolution clarifies that the finding's reference to
"the existing acknowledgeAlarm test" was aspirational — the alarm RPC
surface had zero coverage before this commit.
Client.Java-031 (Conventions): README.md prose lines 17, 22, 26 updated
to use the canonical zb-mom-ww-mxgateway-client / zb-mom-ww-mxgateway-cli
names so the layout description matches Gradle / IDE project names.
Verification: gradle build BUILD SUCCESSFUL; all Java unit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename across every client surface using each language's idiomatic convention:
* .NET clients/dotnet/MxGateway.Client[.Cli|.Tests]/
-> clients/dotnet/ZB.MOM.WW.MxGateway.Client[.Cli|.Tests]/
namespaces -> ZB.MOM.WW.MxGateway.Client[.Cli|.Tests]
contracts ProjectReference repointed to ZB.MOM.WW.MxGateway.Contracts
sln migrated to slnx (dotnet sln migrate)
* Python src/mxgateway -> src/zb_mom_ww_mxgateway
src/mxgateway_cli -> src/zb_mom_ww_mxgateway_cli
distribution: mxaccess-gateway-client -> zb-mom-ww-mxaccess-gateway-client
* Rust crate: mxgateway-client -> zb-mom-ww-mxgateway-client
build.rs proto path repointed
* Java subprojects: mxgateway-{client,cli} -> zb-mom-ww-mxgateway-{client,cli}
packages com.dohertylan.mxgateway -> com.zb.mom.ww.mxgateway
group com.dohertylan.mxgateway -> com.zb.mom.ww.mxgateway
rootProject mxaccessgw-java -> zb-mom-ww-mxaccessgw-java
* Go generate-proto.ps1 proto path repointed; module path and
package mxgateway kept (Go convention).
* proto-inputs.json: generatedOutputs.python updated to new package path.
* scripts/run-client-e2e-tests.ps1: Java CLI install path + gradle task
updated to zb-mom-ww-mxgateway-cli.
CLI binary names (mxgw, mxgw-py, mxgw-go, mxgateway-cli) and wire-level
identifiers (MXGATEWAY_* env vars, the mxgw_<id>_<secret> API key
prefix, protobuf package names like mxaccess_gateway.v1, all MXAccess
references) intentionally NOT renamed.
Fix pre-existing alarms-over-gateway breaks unblocked by the rename:
* mxaccess_gateway.proto: add missing public message QueryActiveAlarmsRequest
{session_id, client_correlation_id, alarm_filter_prefix} and missing
rpc QueryActiveAlarms(QueryActiveAlarmsRequest) returns
(stream ActiveAlarmSnapshot). All four typed clients referenced
these but they were absent from the proto.
* MxAccessGatewayService.QueryActiveAlarms: implement the new RPC on
the server, streaming from IGatewayAlarmService.CurrentAlarms with
optional alarm_filter_prefix filter.
* clients/dotnet/.../DiscoverHierarchyOptions.cs: add the hand-written
.NET POCO that wraps DiscoverHierarchyRequest (referenced by
GalaxyRepositoryClient.DiscoverHierarchyAsync but never authored).
* Drop retired session_id field references from
AcknowledgeAlarmRequest/AcknowledgeAlarmReply test fixtures across
.NET, Rust, Go, and Python clients.
* Rust integration test: add the missing stream_alarms impl on the
fake MxAccessGateway server (the trait gained the method, fake
didn't).
* Rust CLI test: bump expected gatewayProtocolVersion 2 -> 3.
Regenerated artifacts updated in this commit:
* src/ZB.MOM.WW.MxGateway.Contracts/Generated/{MxaccessGateway,MxaccessGatewayGrpc}.cs
* clients/python/src/zb_mom_ww_mxgateway/generated/*_pb2{,_grpc}.py
* clients/go/internal/generated/*.pb.go
(C# regenerated by Grpc.Tools on contracts build; Python and Go via
their generate-proto.ps1 scripts; Rust regenerates from .proto via
tonic-build at compile time so no checked-in artefact.)
Verification: 472 server tests, 275 worker tests (9 dev-rig skipped),
18 integration tests (live MxAccess + LDAP + Galaxy), 57 .NET client
tests, 32 Rust workspace tests, 39 Python tests, all Go packages, and
gradle build for Java all pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Rename 16 kebab-case docs to PascalCase per StyleGuide
- Move per-language client design docs from docs/ to clients/<lang>/
alongside their READMEs
- Add ## Related Documentation sections to 15 docs that lacked one
- Fix sentence-case violations in H3 headings (StyleGuide rule)
- Update cross-references in gateway.md, client READMEs, scripts,
and generate-proto.ps1 helpers to follow the new paths
- Add CLAUDE.md with build/test commands, the source-update
verification matrix, the parity-first contract, and pointers
to MXAccess and Galaxy Repository analysis sources
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>