diff --git a/archreview/remediation/00-tracking.md b/archreview/remediation/00-tracking.md index 90ad8fa..50574af 100644 --- a/archreview/remediation/00-tracking.md +++ b/archreview/remediation/00-tracking.md @@ -64,16 +64,16 @@ Full design + implementation for each row lives in the linked domain doc under i | GWC-03 | High | P0 | S | — | Done | Documented sparse-array max-length bound is unimplemented | | GWC-04 | Medium | P1 | M | — | Done | Full event channel stalls the worker read loop behind command replies | | GWC-05 | Medium | — | S | — | Not started | Worker pipe created with no ACL / no CurrentUserOnly | -| GWC-06 | Medium | P2 | S | GWC-07,08 | Not started | Stopwatch allocated per streamed event | -| GWC-07 | Medium | P2 | S | GWC-06,08 | Not started | Every mapped event is deep-cloned | -| GWC-08 | Medium | P2 | M | GWC-06,07 | Not started | Pipe framing allocates per frame and writes twice | +| GWC-06 | Medium | P2 | S | GWC-07,08 | Done | Stopwatch allocated per streamed event | +| GWC-07 | Medium | P2 | S | GWC-06,08 | Done | Every mapped event is deep-cloned | +| GWC-08 | Medium | P2 | M | GWC-06,07 | Done | Pipe framing allocates per frame and writes twice | | GWC-09 | Medium | — | M | — | Not started | Startup probe is a no-op; its retry pipeline can never retry | | GWC-10 | Medium | — | S | — | Not started | Envelope `sequence` monotonicity specified but not enforced | | GWC-11 | Low | — | S | — | Not started | `_workerClient` written under lock but read lock-free | | GWC-12 | Low | — | S | — | Not started | `WorkerClient.DisposeAsync` not safe against double-dispose | | GWC-13 | Low | — | M | — | Not started | Worker-ready wait is a 25 ms poll loop | -| GWC-14 | Low | P2 | M | — | Not started | Replay ring is a `LinkedList` with a node alloc per event | -| GWC-15 | Low | P2 | S | — | Not started | Per-event gauge reads `ChannelReader.Count` every event | +| GWC-14 | Low | P2 | M | — | Done | Replay ring is a `LinkedList` with a node alloc per event | +| GWC-15 | Low | P2 | S | — | Done | Per-event gauge reads `ChannelReader.Count` every event | | GWC-16 | Low | — | S | — | Not started | `Invoke` resolves the session twice per command | | GWC-17 | Low | — | M | — | Not started | Sessions layer throws `Grpc.Core.RpcException` (layering leak) | | GWC-18 | Low | — | S | — | Not started | `GatewaySession` implements `DisposeAsync` without `IAsyncDisposable` | @@ -116,7 +116,7 @@ Full design + implementation for each row lives in the linked domain doc under i | IPC-02 | Medium | P1 | M | IPC-03 | Done | Worker max frame size hard-coded, cannot follow gateway config | | IPC-03 | Medium | P1 | M | IPC-02 | Done | gRPC max == pipe max with zero headroom; oversized write faults whole session | | IPC-04 | Medium | P1 | S | IPC-03 | Done | `DrainEvents max_events=0` packs entire queue into one reply frame | -| IPC-05 | Medium | P2 | S | — | Not started | Redundant deep copies on command and event hot paths | +| IPC-05 | Medium | P2 | S | — | Done | Redundant deep copies on command and event hot paths | | IPC-06 | Medium | P2 | S | — | Done | `gateway.md` Worker Envelope sketch no longer matches the contract | | IPC-07 | Medium | P2 | S | — | Done | `docs/Grpc.md` says six RPCs; there are seven | | IPC-08 | Medium | — | M | — | Not started | `WorkerCancel` defined and handled but never sent | @@ -124,8 +124,8 @@ Full design + implementation for each row lives in the linked domain doc under i | IPC-10 | Low | — | S | — | Not started | Envelope `sequence` is write-only; monotonicity never validated | | IPC-11 | Low | — | S | — | Not started | No protocol version negotiation despite `supported_protocol_version` name | | IPC-12 | Low | — | S | — | Not started | Gateway frame writer has no write lock; integrity rests on undocumented invariant | -| IPC-13 | Low | P2 | S | IPC-05 | Not started | Gateway writer serializes twice and issues two stream writes | -| IPC-14 | Low | P2 | S | IPC-05 | Not started | Gateway reader allocates fresh array per frame; worker rents from `ArrayPool` | +| IPC-13 | Low | P2 | S | IPC-05 | Done | Gateway writer serializes twice and issues two stream writes | +| IPC-14 | Low | P2 | S | IPC-05 | Done | Gateway reader allocates fresh array per frame; worker rents from `ArrayPool` | | IPC-15 | Low | P2 | M | — | Not started | Worker event delivery 25 ms poll with per-event write+flush, no batching | | IPC-16 | Low | — | S | — | N/A | Correlation id carried twice per reply (Info) | | IPC-17 | Low | P2 | S | — | Done | Two docs name the wrong Python generated-output directory | @@ -253,6 +253,7 @@ Findings the review flagged as one coordinated design pass — sequence them tog | Date | Change | |---|---| +| 2026-07-09 | P2 Wave B — gateway event/frame hot-path performance (8 findings). **Frame I/O (commit `baae59c`):** GWC-08/IPC-13 gateway `WorkerFrameWriter` serializes once into one ArrayPool-rented buffer (LE length prefix + payload) and issues a single write (was: second serialization via `ToByteArray`, separate prefix array, two writes); IPC-14 `WorkerFrameReader` rents the payload buffer from ArrayPool instead of `new byte[]` per frame, read/parse length-bounded, returned in finally. Wire format byte-identical, cross-checked against the worker writer/reader. **Event/command hot path (commit `5e2e40a`):** GWC-06 `StreamEvents` timing via `Stopwatch.GetTimestamp()`/`GetElapsedTime()` (no per-event Stopwatch alloc); GWC-07/IPC-05 `MapEvent` transfers ownership of the inner `MxEvent` instead of cloning (safe — single-consumer `WorkerEvent`, MapEvent runs once pre-fan-out, all downstream consumers read-only; verified no post-mapping mutation) and `CreateCommandEnvelope` drops its redundant second clone (`MapCommand` already isolated the graph); every load-bearing isolation clone kept; GWC-15 `grpc_stream_queue.depth` converted from a per-event push counter to an `ObservableGauge` summing registered channel sources only at scrape time (name/semantics unchanged). **Replay ring (commit `cf1b3d4`):** GWC-14 replaces the `LinkedList` (node alloc per retained event) with a preallocated circular `ReplayEntry[]` (head+count), preserving ascending order, capacity eviction, time trim, oldest-read/ReplayGap math, both query paths, and the lock. → all `Done`. Server build clean (0 warnings); per-cluster tests green (WorkerFrameProtocol 10/10 incl. new near-cap round-trip, EventStream/Metrics/Distributor/Mapper 62/62, Distributor/Replay 33/33 incl. 2 new wrap-around cases). Full-suite macOS checkpoint: 769 passed / 44 failed, all 44 the known named-pipe/fake-worker-harness UDS-104-char limit (0 failures in any touched area). **Pending: windev run** to exercise the frame I/O over a real pipe (the 44 pipe-harness tests are macOS-blind). | | 2026-07-09 | P2 Wave B — SEC-03 (dashboard `__Host-` cookie prefix) → `Done` (commit `7d42c85`). Conditionally restore the prefix instead of a pure doc fix: `DashboardServiceCollectionExtensions` PostConfigure resolves the cookie name as explicit `MxGateway:Dashboard:CookieName` override → else `__Host-MxGatewayDashboard` when `SecurePolicy==Always` (`RequireHttpsCookie` true) → else the plain `MxGatewayDashboard` default; guard never applies `__Host-` without Secure (browsers drop it). `DashboardAuthenticationDefaults.SecureCookieName` added; plain name kept as the non-secure fallback. Five stale docs corrected to the conditional contract (gateway.md, GatewayProcessDesign, ImplementationPlanGateway, GatewayDashboardDesign, CLAUDE.md; GatewayConfiguration phrasing tightened). Test `DashboardCookieOptionsTests` asserts the name flips with `RequireHttpsCookie` and that an override wins. Server build clean; Dashboard tests 149/149. | | 2026-07-09 | P2 Wave A (doc-drift sweep + client version alignment) via parallel agents, on `fix/archreview-p2` off `main`. **Version alignment (commit `4fe1917`):** CLI-18 (.NET `0.1.2`), CLI-21 (Go `ClientVersion` 0.1.0-dev→0.1.2), CLI-26 (Python `__version__`→0.1.2), CLI-29 (Rust `CLIENT_VERSION`→`env!(CARGO_PKG_VERSION)`) → `Done`; verified dotnet build + `go build`/`test` + `cargo check`/`test`/`clippy` + pytest 127 pass. **Doc-drift (commit `06e1046`):** IPC-06/07/21 + TST-13 (gateway.md envelope sketch → proto-as-truth, six→seven RPCs incl. `QueryActiveAlarms` in `docs/Grpc.md`, `Session` RPC marked not-implemented, stale sketches removed), SEC-09 (dashboard `GroupToRole` sample `Administrator` so it passes the validator), SEC-22 (`docs/Authentication.md` rewritten to the shipped `ZB.MOM.WW.Auth.ApiKeys`-package pipeline; 18 stale type names removed, grep-verified absent; named gateway types re-verified present), IPC-17 (wrong Python gen dir `mxgateway`→`zb_mom_ww_mxgateway` in CLAUDE.md + 3 docs), CLI-12 (Java 21→17), CLI-16 (`docs/ClientPackaging.md` reconciled with `.slnx`/Python package name/gradle project names) → `Done`. Docs-only claims verified against source. 13 findings closed. | | 2026-07-09 | Initial tracking doc generated from the six domain remediation designs. All 153 findings `Not started`. |