diff --git a/archreview/2026-07-12/remediation/00-tracking.md b/archreview/2026-07-12/remediation/00-tracking.md
index aeb4c3a..6427536 100644
--- a/archreview/2026-07-12/remediation/00-tracking.md
+++ b/archreview/2026-07-12/remediation/00-tracking.md
@@ -88,7 +88,7 @@ Full design + implementation for each row lives in the linked domain doc under i
| IPC-24 | Medium | P0 | S | — | Not started | CI's unconditional Java churn-revert masks real drift |
| IPC-25 | Medium | P0 | M | — | Not started | Stale Go/Python worker bindings: regenerate (pinned toolchains) + check-codegen Check 4 |
| IPC-26 | Low | P2 | S | WRK-22 (mechanics) | Not started | Cancelled write leaves ghost frame — cancelled means never written |
-| IPC-27 | Low | P2 | S | — | Not started | Descriptor-freshness test blind to enums/services/galaxy descriptor |
+| IPC-27 | Low | P2 | S | — | Done | Descriptor-freshness test blind to enums/services/galaxy descriptor |
| IPC-28 | Low | — | S | — | Done | docs/Grpc.md missing CommandTooLarge → ResourceExhausted mapping |
| IPC-29 | Low | — | S | WRK-26 (discharged by) | Done (discharged by WRK-26) | WorkerFrameProtocol.md missing write-scheduling/sequencing section |
| IPC-30 | Low | P0 | M | WRK-21 (same batch) | Done | Oversized event frame: keep session-fatal, make the death structured |
@@ -176,3 +176,4 @@ Sequence these together rather than piecemeal — several are one change set spa
| 2026-08-07 | Code-review follow-ups on the same branch (commit `6bc3f9b`). (1) **Important** — `ResolveDrainReplyByteBudget` was a step, not a floor: just above the 64 KiB reserve the budget collapsed to a few bytes (exactly 1024 at the validator floor `MaxMessageBytes = 1024 + 64 KiB`), so a byte-heavy `DrainEvents` truncated on every call and the drain-until-empty loop never terminated. Now `Math.Max(frameMax - reserve, frameMax / 2)` — monotonic, never below half the frame max. New test `WorkerPipeSessionTests.DrainEvents_AtValidatorFloorFrameMax_MakesProgressAndTerminates` drives a byte-heavy queue at the exact validator floor and asserts drain-to-empty with no head reported oversized. (2) **Hardening** — the reply-too-large fallback write is now itself size-guarded (`WriteReplyTooLargeFallbackAsync`, shared by the control and STA reply seams) so a pathologically tiny negotiated max below the gateway floor (the WRK-24 gap) cannot make even the backstop session-fatal; log-and-swallow, comment points at WRK-24. (3) **Comment** — corrected the `RepeatedFieldOverheadBytes` docs: `WorkerEvent.CalculateSize()` already includes the event's tag+length, so the 8 bytes is pure slack, not wrapper compensation. **Evidence** — macOS build 0/0, validator filter 4/4. windev (`windev-worker-ci.ps1 -Sha 6bc3f9b -Mode test`, 07:07): x86 Worker build 0/0, `Worker.Tests` **368 passed / 0 failed / 11 skipped**, script exit 0. (An earlier run of the same SHA flaked on the pre-existing `RunAsync_WhenStaActivityIsStale_WritesWatchdogFault` — a 5 s CTS timeout under first-run load, untouched by this change; it passed on the clean re-run and in both prior full runs.) |
| 2026-08-07 | **P1 doc-drift batch → `Done`: TST-27, WRK-26 (discharges IPC-29), CLI-42, CLI-43, IPC-28** (branch `fix/doc-drift-batch`). Doc-only; no source, proto, or test changes — cross-checked against HEAD in this worktree. **TST-27**: `docs/GatewayConfiguration.md`'s `ShowTagValues` row no longer says "Reserved" — it now states what `false` (default) does (`DashboardEventBroadcaster` blanks tag values from a deep-cloned `MxEvent` before the SignalR events-hub mirror, metadata still renders), the security relevance (the per-session hub ACL, SEC-25 roadmap item 12, still does not exist, so this redaction is the only thing between a low-trust Viewer and other sessions' tag values), and the honest scope limit (the flag does **not** cover `/browse`). **WRK-26** (discharges **IPC-29**): `docs/MxAccessWorkerInstanceDesign.md`'s "Outbound Queues" section rewritten from the stale five-level priority list to the two-class `Control`/`Event` scheduler actually shipped (`WorkerFrameWriter`/`WorkerFrameWritePriority.cs`), with the collapsed-decision rationale recorded, and the overflow paragraph rewritten to the implemented fail-fast (`WorkerFault` category `QueueOverflow` → fault frame written → `RunAsync` unwinds → generic `WorkerExitCode.UnexpectedFailure`, dedicated code still open). `docs/WorkerFrameProtocol.md` gained a new "Write Scheduling And Sequencing" section: the two priority classes, enqueue-then-contend/single-lock-holder-drains-all, write-time peek-stamp-commit sequencing, per-frame-rejection vs. stream-failure semantics, and flush coalescing — stated truthfully as landed (WRK-23's peek-stamp-commit is live at HEAD) or not (the drain loop still awaits each event `WriteAsync` individually, so WRK-25's N-events-one-flush batching has **not** landed and the section says so explicitly). Cancellation is deliberately **not** documented as a firm contract — a one-paragraph placeholder notes it is pending WRK-22, which has not landed (confirmed by reading `WorkerFrameWriter.cs`: no `Claimed`/tombstone machinery exists yet). `gateway.md:328-330` was cross-checked and left unchanged — its sequence prose (both sides stamp at write, per GWC-28) already reads true. **CLI-42**: `clients/rust/README.md` and `docs/ClientPackaging.md`'s Rust section now document the vendored proto layout matching `clients/rust/build.rs` exactly — repo-path-first resolution (`../../src/ZB.MOM.WW.MxGateway.Contracts/Protos`) falling back to `clients/rust/protos/` when the canonical path is absent (published-tarball case), the same-commit refresh rule enforced by `scripts/check-codegen.ps1` Check 3, and why `cargo package`/`cargo publish` run without `--no-verify` (matches `scripts/pack-clients.ps1:190-192`). **CLI-43**: `docs/style-guides/JavaStyleGuide.md` line 8 now says "Target Java 17 (the Ignition 8.3 baseline...)" mirroring the CLI-12 wording, matching the shipped `clients/java/build.gradle` toolchain-17 build. **IPC-28**: `docs/Grpc.md`'s exception-mapping prose gained `CommandTooLarge` → `ResourceExhausted` (verified against the live `switch` in `Grpc/MxAccessGatewayService.cs:950-960`), and the `Invoke` section gained one sentence on the oversized-payload path (`WorkerClient.InvokeAsync` rejects at the enqueue boundary per-correlation, session not faulted — verified against `WorkerClient.cs:220-234`), cross-referencing the headroom rule already documented in `docs/GatewayConfiguration.md:120-129`. Did not touch the DrainEvents-truncation row or the proto/`Generated/` trees — those belong to a parallel codegen task per the handoff note. **Source files cross-read for accuracy** (no edits): `src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameWriter.cs`, `.../WorkerFrameWritePriority.cs`, `.../WorkerPipeSession.cs` (confirmed two-class scheduler, WRK-21/23/28/30 landed, WRK-25/WRK-22 not landed), `src/ZB.MOM.WW.MxGateway.Worker/WorkerApplication.cs` (exit-code mapping), `src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventQueue.cs` (overflow fault path), `src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/DashboardEventBroadcaster.cs` + `Configuration/DashboardOptions.cs` + `docs/GatewayDashboardDesign.md:170` (ShowTagValues), `src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs:940-963` + `Workers/WorkerClient.cs:205-244` + `Workers/WorkerClientErrorCode.cs` (CommandTooLarge mapping), `clients/rust/build.rs`, `clients/rust/Cargo.toml`, `scripts/check-codegen.ps1`, `scripts/pack-clients.ps1` (Rust vendoring), `gateway.md:326-360` (sequence-prose cross-check). Verification (greps, doc-only — no build required): `grep -n 'Reserved' docs/GatewayConfiguration.md` no longer matches the `ShowTagValues` row; `grep -n 'faults' docs/MxAccessWorkerInstanceDesign.md` shows no remaining five-level list; `grep -n 'scheduling' docs/WorkerFrameProtocol.md` finds the new section; `grep -rn 'Java 21' docs/style-guides/` empty; `grep -i vendored docs/ClientPackaging.md clients/rust/README.md` non-empty in both; `grep -n 'CommandTooLarge' docs/Grpc.md` shows the mapping. |
| 2026-08-07 | **IPC-23 + IPC-24 + IPC-25 + IPC-32 → `Done`; GWC-25 deferred proto-comment resolved** (branch `fix/ipc-24-25-codegen`). The proto-comment/doc + regen wave for the P0 codegen-freshness cluster. **Proto comments (comment-only):** `mxaccess_worker.proto` `GatewayHello.max_frame_bytes` gained the every-frame-must-fit / reply-builders-truncate sentence (IPC-23); `mxaccess_gateway.proto` `DrainEventsReply` gained the count-cap+byte-cap / drain-until-empty comment (IPC-23); `mxaccess_gateway.proto` `ReplayGap.oldest_available_sequence` gained the empty-ring value definition (`highest observed + 1`, `oldest − 1` formula stays valid) resolving GWC-25's deferred amendment. **Regen wave:** `Contracts/Generated/{MxaccessGateway,MxaccessWorker}.cs` (XML-doc only, no descriptor delta), `clients/rust/protos/{mxaccess_gateway,mxaccess_worker}.proto` byte-copied, Go `internal/generated/{mxaccess_gateway,mxaccess_worker}.pb.go` (worker binding was genuinely stale — it lacked the whole `MaxFrameBytes` field/accessor/rawDesc, now refreshed; gateway.pb.go got the new doc comments), Python `generated/mxaccess_worker_pb2.py` (real descriptor delta — `max_frame_bytes` field + cascaded offsets), Java aggregates `MxaccessGateway.java`/`MxaccessWorker.java` (javadoc from the new proto comments — **zero** protobuf-version churn under the pinned toolchain), and the client descriptor set `mxaccessgw-client-v1.protoset`. **IPC-24:** regenerating Java on the pinned toolchain (grpc 1.76.0 / protobuf 4.33.1, Homebrew JDK 17) produced only the legitimate javadoc delta with no `validateProtobufGencodeVersion`/`major=/minor=/patch=` churn — so the preferred path was taken: the unconditional churn-revert step and its comment were **deleted** from `.gitea/workflows/ci.yml` (the `git diff --exit-code` gate is now a true message-level drift gate for the single-file Java aggregates). **IPC-25:** pinned the Go generators in `clients/go/generate-proto.ps1` (`protoc-gen-go` v1.36.11 assert, `protoc-gen-go-grpc` 1.6.2 assert, protoc warn-only; also fixed a latent `(if …)`→`$(if …)` parse bug that broke the script under pwsh 7); added **Check 4** to `scripts/check-codegen.ps1` (regenerate Go+Python bindings, fail on any diff, tool-missing FAILS not skips) and relabeled the banners `1/4`…`4/4` + header comment (**IPC-32** folded in); added the pinned-generator installs to the `portable` CI job before the codegen step. **Docs same commit:** `ClientProtoGeneration.md` (pinned-versions table + Go/Python Check-4 guard + Java-revert-deleted note), `Contracts.md` (four-check enumeration), `GatewayTesting.md` (java job + four-check prose), `clients/java/zb-mom-ww-mxgateway-client/build.gradle` checkGeneratedClean caveat. **IPC-23 DrainEvents-truncation doc deviation:** `docs/Grpc.md` has no DrainEvents row (it documents only the seven public RPCs; DrainEvents is a worker diagnostic), and `docs/WorkerFrameProtocol.md` (owned by the parallel doc batch) already carries the byte-cap/drain-until-empty prose via WRK-21 — so no Grpc.md edit was made. **Evidence (macOS):** `pwsh scripts/check-codegen.ps1` all four checks green (banners `1/4`…`4/4`); `dotnet build …NonWindows.slnx` 0 warnings/0 errors; `dotnet test …Tests --filter ~ClientProtoInputTests` green; `grep -c max_frame_bytes` → Go worker 2, Python worker 1 (both non-zero, were 0); `clients/go` gofmt clean + `go build` + `go test` ok; `clients/python` `pytest` 163 passed/1 skipped; Java `gradle generateProto` clean under JDK 17. **Negative-path proof:** added a scratch field to `mxaccess_worker.proto`, regenerated only `Contracts/Generated/`, reran `check-codegen.ps1` and confirmed **Check 4 fails naming both `clients/go/internal/generated` and `clients/python/…/generated`**, then reverted. **Not published** (CLI-39 gates Go/Python republish). |
+| 2026-08-07 | **IPC-27 → `Done`** (branch `fix/ipc-27-descriptor-test`). `ClientProtoInputTests.Descriptor_ContainsEveryContractMessageAndField` renamed to `Descriptor_ContainsEveryContractSymbol` and extended: the reflection walk now also collects enums (top-level `file.EnumTypes` + nested `message.EnumTypes`, as `{enumFullName}` and `{enumFullName}/{valueName}`) and services/methods (`file.Services`, as `{serviceFullName}` and `{serviceFullName}/{methodName}`), and `GalaxyRepositoryReflection.Descriptor` was added to the enumerated contract files alongside the existing gateway/worker descriptors. The published-side `CollectPublishedSymbols` grew matching walks over `FileDescriptorProto.EnumType`/`Service` and `DescriptorProto.EnumType`; the comparison stays a flat, order-insensitive string-set diff with no protoc dependency. Docs: `docs/ClientProtoGeneration.md` and `docs/Contracts.md` updated from "message or field" to full symbol coverage (message/field/enum/enum value/service/method), both now naming `galaxy_repository.proto` explicitly. **Red-path proof performed:** pointed the test at the pre-IPC-01 stale protoset (`git cat-file -p 0f88a95:clients/proto/descriptors/mxaccessgw-client-v1.protoset`), confirmed it failed naming — among the missing symbols — `mxaccess_worker.v1.GatewayHello/max_frame_bytes`, enum values (`MxCommandKind/MX_COMMAND_KIND_SUBSCRIBE_ALARMS`, `AlarmProviderMode/*`, `MxEventFamily/MX_EVENT_FAMILY_ON_ALARM_PROVIDER_MODE_CHANGED`), services/methods (`MxAccessGateway/StreamAlarms`, `GalaxyRepository/BrowseChildren`), and the full `galaxy_repository.v1.*` surface; restored the real descriptor path afterward and re-ran green. Verification: `dotnet build src/ZB.MOM.WW.MxGateway.NonWindows.slnx` 0 warnings/0 errors; `dotnet test …Tests --filter FullyQualifiedName~ClientProtoInputTests` 5/5 passed. No proto, config, or CI change, matching the design's stated scope. |
diff --git a/archreview/2026-07-12/remediation/30-contracts-ipc.md b/archreview/2026-07-12/remediation/30-contracts-ipc.md
index 7b66eda..ce6467d 100644
--- a/archreview/2026-07-12/remediation/30-contracts-ipc.md
+++ b/archreview/2026-07-12/remediation/30-contracts-ipc.md
@@ -16,7 +16,7 @@ All `path:line` citations were re-verified against the working tree at `4f5371f`
| IPC-24 | Medium | P0 | S | — | Done | CI's unconditional Java churn-revert masks real generated-code drift for message-level proto changes |
| IPC-25 | Medium | P0 | M | — | Done | Committed Go/Python worker bindings are stale at HEAD; no guard covers them |
| IPC-26 | Low | P2 | S¹ | WRK-22 | Not started | Cancelled write leaves a ghost frame that is still written (contract requirement here; fix mechanics in WRK-22) |
-| IPC-27 | Low | P2 | S | — | Not started | Descriptor freshness test blind to enums, enum values, services/methods, and the Galaxy contract |
+| IPC-27 | Low | P2 | S | — | Done | Descriptor freshness test blind to enums, enum values, services/methods, and the Galaxy contract |
| IPC-28 | Low | — | S | — | Done | `docs/Grpc.md` omits the `CommandTooLarge` → `ResourceExhausted` mapping |
| IPC-29 | Low | — | S | — | Done (discharged by WRK-26) | Worker writer priority scheduling and write-time sequence stamping undocumented in the frame-protocol doc |
| IPC-30 | Low | P0 | M | WRK-21 (same file/batch) | Done | Oversized worker→gateway event frame is session-fatal — make the death deliberate, structured, and diagnosable |
diff --git a/docs/ClientProtoGeneration.md b/docs/ClientProtoGeneration.md
index 270ccf5..9985ffd 100644
--- a/docs/ClientProtoGeneration.md
+++ b/docs/ClientProtoGeneration.md
@@ -74,10 +74,12 @@ protoc-version encoding drift and does not false-fail across protoc releases
(it warns, rather than fails, when protoc is off the pin).
The gateway test project carries an independent, protoc-free freshness guard:
-`ClientProtoInputTests.Descriptor_ContainsEveryContractMessageAndField` reflects
-over the in-process contract descriptors and fails if any contract message or
-field is missing from the committed protoset. This is the primary CI gate for
-descriptor staleness; a red test means "regenerate and commit the protoset."
+`ClientProtoInputTests.Descriptor_ContainsEveryContractSymbol` reflects over the
+in-process contract descriptors — `mxaccess_gateway.proto`, `mxaccess_worker.proto`,
+and `galaxy_repository.proto` — and fails if any contract message, field, enum,
+enum value, service, or method is missing from the committed protoset. This is
+the primary CI gate for descriptor staleness; a red test means "regenerate and
+commit the protoset."
### Pinned generator versions
diff --git a/docs/Contracts.md b/docs/Contracts.md
index fc53b25..cfb14a3 100644
--- a/docs/Contracts.md
+++ b/docs/Contracts.md
@@ -156,10 +156,11 @@ pwsh -File scripts/publish-client-proto-inputs.ps1
Freshness is guarded two ways so a skipped regeneration cannot ship silently:
-- `ClientProtoInputTests.Descriptor_ContainsEveryContractMessageAndField` (gateway test project)
- reflects over the in-process contract descriptors and fails if any message or field is missing
- from the committed protoset. It is semantic (symbol presence), needs no protoc, and runs in the
- Linux CI. A red test means "regenerate and commit the protoset."
+- `ClientProtoInputTests.Descriptor_ContainsEveryContractSymbol` (gateway test project)
+ reflects over the in-process contract descriptors — including `galaxy_repository.proto` — and
+ fails if any message, field, enum, enum value, service, or method is missing from the committed
+ protoset. It is semantic (symbol presence), needs no protoc, and runs in the Linux CI. A red
+ test means "regenerate and commit the protoset."
- `pwsh -File scripts/publish-client-proto-inputs.ps1 -Check` rebuilds the descriptor and compares
it to the committed one. The comparison normalizes both sides through the same protoc with
`source_code_info` stripped, so it does not false-fail across protoc releases.
diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientProtoInputTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientProtoInputTests.cs
index b047347..8af2d58 100644
--- a/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientProtoInputTests.cs
+++ b/src/ZB.MOM.WW.MxGateway.Tests/Contracts/ClientProtoInputTests.cs
@@ -3,22 +3,24 @@ using Google.Protobuf;
using Google.Protobuf.Reflection;
using ZB.MOM.WW.MxGateway.Contracts;
using ZB.MOM.WW.MxGateway.Contracts.Proto;
+using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
namespace ZB.MOM.WW.MxGateway.Tests.Contracts;
public sealed class ClientProtoInputTests
{
///
- /// Guards the published client descriptor set against silent staleness. Every message
- /// and field compiled into the in-process contract (which the build regenerates from the current
- /// .proto sources) must appear in the committed protoset. A missing symbol means the
- /// descriptor was not regenerated after a proto change; run
- /// scripts/publish-client-proto-inputs.ps1 and commit the refreshed protoset.
+ /// Guards the published client descriptor set against silent staleness. Every message,
+ /// field, enum, enum value, service, and method compiled into the in-process contract
+ /// (which the build regenerates from the current .proto sources) must appear in the
+ /// committed protoset. A missing symbol means the descriptor was not regenerated after a
+ /// proto change; run scripts/publish-client-proto-inputs.ps1 and commit the
+ /// refreshed protoset.
/// The check is semantic (symbol presence) rather than byte-wise, so it is independent of protoc
/// version and does not require protoc on the test runner.
///
[Fact]
- public void Descriptor_ContainsEveryContractMessageAndField()
+ public void Descriptor_ContainsEveryContractSymbol()
{
DirectoryInfo repositoryRoot = FindRepositoryRoot();
string descriptorPath = Path.Combine(
@@ -34,20 +36,65 @@ public sealed class ClientProtoInputTests
HashSet publishedMessages = new(StringComparer.Ordinal);
HashSet publishedFields = new(StringComparer.Ordinal);
+ HashSet publishedEnums = new(StringComparer.Ordinal);
+ HashSet publishedServices = new(StringComparer.Ordinal);
foreach (FileDescriptorProto file in descriptorSet.File)
{
foreach (DescriptorProto message in file.MessageType)
{
- CollectPublishedSymbols(file.Package, message, publishedMessages, publishedFields);
+ CollectPublishedSymbols(file.Package, message, publishedMessages, publishedFields, publishedEnums);
+ }
+
+ foreach (EnumDescriptorProto enumType in file.EnumType)
+ {
+ CollectPublishedEnumSymbols(file.Package, enumType, publishedEnums);
+ }
+
+ foreach (ServiceDescriptorProto service in file.Service)
+ {
+ string serviceFullName = string.IsNullOrEmpty(file.Package) ? service.Name : file.Package + "." + service.Name;
+ publishedServices.Add(serviceFullName);
+ foreach (MethodDescriptorProto method in service.Method)
+ {
+ publishedServices.Add(serviceFullName + "/" + method.Name);
+ }
}
}
List missing = [];
- foreach (FileDescriptor file in new[] { MxaccessGatewayReflection.Descriptor, MxaccessWorkerReflection.Descriptor })
+ FileDescriptor[] contractFiles =
+ [
+ MxaccessGatewayReflection.Descriptor,
+ MxaccessWorkerReflection.Descriptor,
+ GalaxyRepositoryReflection.Descriptor,
+ ];
+ foreach (FileDescriptor file in contractFiles)
{
foreach (MessageDescriptor message in file.MessageTypes)
{
- CollectMissingContractSymbols(message, publishedMessages, publishedFields, missing);
+ CollectMissingContractSymbols(message, publishedMessages, publishedFields, publishedEnums, missing);
+ }
+
+ foreach (EnumDescriptor enumType in file.EnumTypes)
+ {
+ CollectMissingEnumSymbols(enumType, publishedEnums, missing);
+ }
+
+ foreach (ServiceDescriptor service in file.Services)
+ {
+ if (!publishedServices.Contains(service.FullName))
+ {
+ missing.Add(service.FullName);
+ }
+
+ foreach (MethodDescriptor method in service.Methods)
+ {
+ string key = service.FullName + "/" + method.Name;
+ if (!publishedServices.Contains(key))
+ {
+ missing.Add(key);
+ }
+ }
}
}
@@ -62,7 +109,8 @@ public sealed class ClientProtoInputTests
string package,
DescriptorProto message,
HashSet messages,
- HashSet fields)
+ HashSet fields,
+ HashSet enums)
{
string fullName = string.IsNullOrEmpty(package) ? message.Name : package + "." + message.Name;
messages.Add(fullName);
@@ -74,7 +122,26 @@ public sealed class ClientProtoInputTests
foreach (DescriptorProto nested in message.NestedType)
{
- CollectPublishedSymbols(fullName, nested, messages, fields);
+ CollectPublishedSymbols(fullName, nested, messages, fields, enums);
+ }
+
+ foreach (EnumDescriptorProto enumType in message.EnumType)
+ {
+ CollectPublishedEnumSymbols(fullName, enumType, enums);
+ }
+ }
+
+ private static void CollectPublishedEnumSymbols(
+ string containingScope,
+ EnumDescriptorProto enumType,
+ HashSet enums)
+ {
+ string enumFullName = string.IsNullOrEmpty(containingScope) ? enumType.Name : containingScope + "." + enumType.Name;
+ enums.Add(enumFullName);
+
+ foreach (EnumValueDescriptorProto value in enumType.Value)
+ {
+ enums.Add(enumFullName + "/" + value.Name);
}
}
@@ -82,6 +149,7 @@ public sealed class ClientProtoInputTests
MessageDescriptor message,
HashSet publishedMessages,
HashSet publishedFields,
+ HashSet publishedEnums,
List missing)
{
if (!publishedMessages.Contains(message.FullName))
@@ -100,7 +168,32 @@ public sealed class ClientProtoInputTests
foreach (MessageDescriptor nested in message.NestedTypes)
{
- CollectMissingContractSymbols(nested, publishedMessages, publishedFields, missing);
+ CollectMissingContractSymbols(nested, publishedMessages, publishedFields, publishedEnums, missing);
+ }
+
+ foreach (EnumDescriptor enumType in message.EnumTypes)
+ {
+ CollectMissingEnumSymbols(enumType, publishedEnums, missing);
+ }
+ }
+
+ private static void CollectMissingEnumSymbols(
+ EnumDescriptor enumType,
+ HashSet publishedEnums,
+ List missing)
+ {
+ if (!publishedEnums.Contains(enumType.FullName))
+ {
+ missing.Add(enumType.FullName);
+ }
+
+ foreach (EnumValueDescriptor value in enumType.Values)
+ {
+ string key = enumType.FullName + "/" + value.Name;
+ if (!publishedEnums.Contains(key))
+ {
+ missing.Add(key);
+ }
}
}