0.2.3's Secrets.Ui ships ConfirmDeleteModal's own styles under
collision-proof zb-secrets-* class names. This host links no Bootstrap so
it never exhibited the invisible-modal defect, but it takes the fixed
line for parity; also rides over 0.2.1/0.2.2 (Akka-replicator fixes -
inert here, no replicator in use). Tests: 780 pass, 45 fail on macOS both
before and after the bump (NamedPipeServerStream multi-instance is
Windows-only - the fake-worker pipe harness cannot run on this platform);
zero delta from the bump.
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
The suppression's own comment said 'Remove once an upstream fix ships'. It has:
SQLitePCLRaw.lib.e_sqlite3 2.1.12 patches this advisory within the 2.1.x line, so
the premise that no patched release existed is no longer true.
The Server project already resolved 2.1.12, but only incidentally - transitively via
ZB.MOM.WW.Auth.ApiKeys. An explicit PackageReference makes that floor intentional, so
a change to the Auth dependency graph cannot silently regress to the vulnerable 2.1.11.
Removing the suppression restores auditing for exactly that case.
Verified: forced restore of the NonWindows solution reports no NU1903; build is
0 warnings / 0 errors; test results unchanged from baseline (781 passed, 44 failed -
all 44 pre-existing macOS Unix-domain-socket path-length failures in the fake-worker
harness, identical count before and after).
Version hygiene + picks up the G-8 KEK-rotation surface.
NOT a security fix for this repo. An earlier version of this message claimed it
closed GHSA-2m69-gcr7-jv3q; that was wrong. A/B against the 0.1.2 baseline shows
SQLitePCLRaw.lib.e_sqlite3 already resolved 2.1.12, supplied transitively by the
pre-existing ZB.MOM.WW.Auth.ApiKeys 0.1.5 reference.
Note: src/Directory.Build.props still suppresses GHSA-2m69-gcr7-jv3q on the
now-outdated rationale that no patched e_sqlite3 exists. 2.1.12 is patched and
already resolving, so that suppression looks removable - verify separately.
Auth.ApiKeys 0.1.4 pulled SQLitePCLRaw.lib.e_sqlite3 2.1.11, which carries
high-severity advisory GHSA-2m69-gcr7-jv3q. This gateway was genuinely exposed --
verified 2.1.11 resolving before the bump and 2.1.12 after, with the vulnerability
scan now clean.
Auth 0.1.5 is 0.1.4 plus a transitive pin, so there is no API change here.
Suite unchanged from the documented baseline: 781 pass / 44 pre-existing worker-COM
failures on macOS, none Auth/ApiKey/SQLite related.
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
The nightly (run #47) portable job failed CreateAsync_WhenFakeWorkerNeverSendsReady
_TimesOutAndKillsWorker: it asserts the factory's TimeoutException carries 'did not
complete startup' (thrown at the session's 1s startup timeout), but the test's
.WaitAsync(TestTimeout=5s) anti-hang net tripped first under CI load and surfaced
.NET's generic 'The operation has timed out.' — same commit passed on the push run
(#46), so it's a load-sensitive timeout race, not a regression. Raising runner
capacity to 4 makes concurrent-load flakes like this more likely.
Fix: give the two CreateAsync failure/timeout tests a dedicated 30s HangGuardTimeout,
far above the 1s semantic timeout under test, so the factory's own exception is
always the one observed. The net still catches a genuine hang.
Note: this class cannot run on macOS (named pipes -> Unix domain sockets fail on
the pipe path); verification is via CI Linux.
Surfaced during TST-25 acceptance verification: all CI runs on one shared
gitea-runner (maxParallel=1, co-located 10.100.0.35) interleaved with
dohertj2/lmxopcua, and Gitea 1.26 has no run cancel/delete API, so queue
latency is unbounded under cross-repo contention and the runner is a single
point of failure. Design: add a second/labelled runner; document the no-cancel
reality and the run-windev-ci.sh queue-bypass. Low/P2. Roll-ups updated.
Fixes found by running the TST-25 CI acceptance checks:
- CI SSH private key no longer leaks in windows-x86 logs (base64 secret + decode;
key rotated on windev). Masking confirmed (WINDEV_SSH_KEY: *** on run #38).
- Bootstrap git fetch/checkout now runs under the worktree lock, so concurrent
runs serialize instead of colliding on .git/index.lock.
- Deflaked SessionManagerTests fail-fast timing assertions (portable flake).
Merge target df7e20d verified GREEN via the local windev path (x86 Worker build
+ Worker.Tests 356 passed); main's own CI run provides the canonical evidence.
CI (portable, run #38) intermittently failed SessionManagerTests
.InvokeAsync_WhenTimeoutZero_FailsFastUnchanged with 'expected immediate
fail-fast but took 155ms'. The fail-fast paths throw synchronously under the
lock (GatewaySession.GetReadyWorkerClientAsync) and never enter the poll loop,
so the absolute <100ms wall-clock bound measured only host load, not behavior,
and flaked under CI contention.
- WhenWorkerFaulted_FailsFastWithBothStates: anchor the bound to a large (5000ms)
ready-wait timeout and assert fail-fast returns in < timeout/3 — a regression
that burned the timeout is still caught, but scheduling jitter can't trip it.
- WhenTimeoutZero_FailsFastUnchanged: drop the wall-clock assertion entirely
(a zero timeout has no wait window to burn); the error code, both-states
message, and InvokeCount == 0 already pin the immediate fail-fast.
Verified: the 3 SessionManager timing tests pass (net10.0).
Acceptance-check finding (concurrency): the worktree lock in windev-worker-ci.ps1
guarded the build stage, but run-windev-ci.sh's bootstrap did git fetch + git
checkout on the shared C:\build\mxaccessgw-ci clone BEFORE that lock was taken.
Two concurrent runs therefore collided on .git/index.lock at the bootstrap stage
(the second failed 'Another git process seems to be running', exit 1) instead of
the second waiting — defeating the lock's purpose for manual/degraded-mode overlap
or a future second runner. (The single Gitea runner serializes jobs, so CI pushes
never actually overlapped; this is defense-in-depth being restored.)
Fix: the bootstrap now acquires the same mkdir worktree lock around its fetch/
checkout and exports MXGW_CI_LOCK_HELD; windev-worker-ci.ps1 re-uses that lock
(skips re-acquire/release) when the env is set, and still self-locks for a
standalone/manual run. Now the second concurrent run waits at the bootstrap.
The windows-x86 acceptance check 'no key material in logs' failed: run #37's
job log printed the full WINDEV_SSH_KEY PEM in the step env echo. Gitea's
secret masker is line-oriented, so a multiline PEM rendered as one line with
literal \n escapes never matches the real-newline secret value and is not
redacted.
Fix: store WINDEV_SSH_KEY base64-encoded (single line) so the masker redacts
it to ***; run-windev-ci.sh auto-decodes a base64 PEM (still accepts a raw PEM
for local hand-testing). Drop the redundant WINDEV_SSH_KNOWN_HOSTS from the job
env (host keys are public and come from the committed windev.known_hosts pin),
removing another cleartext env line. Document the base64 requirement in the
bring-up README.
Operationally: the previously-exposed CI key has been rotated on windev
(old pubkey revoked from administrators_authorized_keys, new key installed) and
the Gitea WINDEV_SSH_KEY secret replaced with the new key's base64.
Restores the Windows/x86 test tier that had zero automation since the native
windows CI jobs were removed. A Linux CI job SSHes to windev (10.100.0.48),
checks out the pushed SHA in an isolated locked clone, and runs the x86 Worker
build + Worker.Tests (per push) / live-MXAccess smoke (nightly). Verified GREEN
under Gitea run #37 on d769244.
Operator bring-up complete and the SSH-driven Windows/x86 CI tier passed
green end-to-end under Gitea Actions run #37: the Linux runner SSHed windev,
checked out the SHA in the isolated C:\build\mxaccessgw-ci clone under lock,
built the x86 Worker and ran Worker.Tests (exit 0); nightly-windev correctly
skipped on the push event. Flip TST-25/TST-26 status rows to Done and record
the bring-up + green-run evidence in the change log.
The `windows`/`live-mxaccess` CI jobs were removed in abb0930 because Gitea
act_runner host-mode on Windows is broken and a runs-on gate with no runner
wedges the queue. This left the entire x86/net48 Worker + Worker.Tests tier
(and the live-MXAccess smoke) unguarded by CI.
Restore it without a native Windows runner: Linux jobs on ubuntu-latest (which
always schedule) SSH to windev (10.100.0.48), fetch+checkout the SHA under test
in an isolated clone C:\build\mxaccessgw-ci, run the x86 build/tests there, and
propagate the remote exit code back — so a Worker regression turns the job red
and an unreachable host fails loud (never stuck).
- scripts/ci/run-windev-ci.sh: Linux driver (key/known-hosts, UTF-16LE base64
EncodedCommand bootstrap, ssh, exit-code passthrough).
- scripts/ci/windev-worker-ci.ps1: windev stage — worktree lock, re-fetch/
checkout under lock, build|test|live modes; PS 5.1-safe, checks $LASTEXITCODE.
- scripts/ci/windev.known_hosts: pinned host keys for StrictHostKeyChecking.
- scripts/ci/README.md: operator bring-up + acceptance checklist.
- ci.yml: windows-x86 (per-push `test`) + nightly-windev (scheduled `live` +
on-failure Gitea issue); drop the removal note; fix header/cron comments.
TST-26 (same commit, by rule): correct docs/scripts that still describe the
removed jobs — GatewayTesting.md, Contracts.md, check-codegen.ps1 — and
reattribute the Generated/-commit guard (primary = check-codegen diff in the
portable job; secondary = windows-x86 net48 compile).
Mechanism hand-verified on windev: build->0, bogus-SHA->nonzero (lock released),
test->356 passed/0 failed in ~50s, ssh exit-code propagation confirmed. Merge
requires operator bring-up first (dedicated ci@ key + Gitea secrets) or the
per-push job is red every push — see scripts/ci/README.md.
Six-domain re-review at the P2 merge (4f5371f): all prior Done claims
verified (none false, 10 partial), 47 new findings (1 High, 14 Medium),
with per-finding design/implementation plans and a new tracking register.
Resolve all CommentChecker findings across the gateway server, worker, tests,
and .NET client (314 -> 0 real issues): add missing <returns>/<summary>/<param>
on public and test members, convert Stream/interface overrides to <inheritdoc/>,
and remove internal task/issue tracking IDs (SEC-*, IPC-*, WRK-*, GWC-*, TST-*,
Client.Dotnet-*) from shipped code documentation while preserving the design
rationale prose. Shipped comments should not carry internal bookkeeping, and
complete XML docs keep the analyzer/TreatWarningsAsErrors gate and generated API
docs clean. The 6 remaining flags are heuristic false positives (MD5, UTC-4,
capacity-1, near-1601) left intact so real documentation is not corrupted.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
With no working Windows runner (act host-mode on Windows is broken), the windows +
live-mxaccess jobs never resolve to 'skipped' (that only happens when a matching
runner exists) — they sit 'queued' and block every run from completing. Remove them
so portable + java are the whole pipeline and runs finish clean-green. x86 Worker and
live-MXAccess verification stay on the manual windev worktree process.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Make the authored CI pipeline execute and go green on the co-located runner (portable + java), fix the five real latent defects the first real run surfaced (codegen-check null, stale rust proto, orphan-terminator Linux path, stale java worker codegen, py3.12 event loop), provision pwsh + Gradle for the self-hosted act image, and disable the Windows jobs (act host-mode broken). TST-03 -> Done.
https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Add docs/plans/2026-07-10-dashboard-session-acl-tst15.md: the fleshed-out
Phase-4 design for the deferred TST-15 finding. Resolves the crux the deferral
left open — the dashboard authenticates LDAP users (Admin/Viewer) while sessions
are API-key-owned (OwnerKeyId), two disjoint identity domains — via a session tag
sourced from the owning API key (carried in the existing ApiKeyConstraints JSON
blob, no SQLite migration). Admin-sees-all; a Viewer may SubscribeSession iff
session.Tags intersects the Viewer's granted tags (new Dashboard:GroupToTag map
-> hub-token tag claims); untagged sessions Admin-only by default. Includes the
enforcement path, task breakdown (epic Tasks 16-19), test plan incl. live-LDAP,
and rejected alternatives.
Design only — TST-15 stays Not started (no implementation). The tracker and the
60-testing-docs-gaps TST-15 section point at the design doc; the change-log also
records the TST-03 finding (zero registered runners; needs a runner co-located on
the gitea Docker network).
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
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
Java client toolchain (homebrew openjdk@17) works on the Mac, so the remaining
Java halves of CLI-15/CLI-04 are done locally this session, not batched to windev.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Resolve the session-resilience epic's shipped-vs-planned entanglement:
- Phase 3 (reconnect) finished: Task 13 = TST-02 (owner-scoped attach, P0),
Task 15 = TST-01 (reconnect integration test), Task 14 = CLI-15 for 4/5
clients (Java pending, windev batch).
- Phase 4 (per-session dashboard ACL) scoped as TST-15; the open Viewer-default
decision is settled: admin-sees-all, Viewer strict per owned/granted session
(matches TST-02's gRPC owner binding).
- Phase 5 (orphan-worker reattach) marked DEFERRED, not planned. The
EnableOrphanReattach flag does not exist and must not be referenced as if it
does. The CLAUDE.md "gateway restart does not reattach orphan workers"
invariant stands.
Updates oldtasks.md, the tasks.json mirror (statuses + governance note), and the
CLAUDE.md reconnect paragraph (clients now consume ReplayGap; reattach deferred).
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Proves the default-on reconnect protocol end to end through the real gRPC
StreamEvents path via the fake worker harness (no live COM). Two facts:
- ReconnectInsideRetainedWindow_ReplaysTailNoGap: capacity 16 retains all
events; reconnect from a mid-batch after_worker_sequence cursor replays
exactly the events with WorkerSequence > cursor (retained tail + events
emitted while detached), strictly ascending, distinct, no ReplayGap.
- ReconnectWithStaleCursor_EmitsReplayGapSentinelFirst: capacity 3 with 6
events forces eviction; reconnect with AfterWorkerSequence=1 yields the
ReplayGap sentinel first (Family=Unspecified, no body, RequestedAfterSequence=1,
OldestAvailableSequence=oldest retained), then the retained tail ascending.
Single-subscriber mode: the first stream is fully detached (cancel + await the
stream task runs EventStreamService's finally, dropping the subscriber count to
0) before reconnect; the distributor + replay ring are created once per session
and survive detach, so events emitted while detached are retained.
macOS note: fake-worker E2E tests need TMPDIR=/tmp (default macOS TMPDIR pushes
the CoreFxPipe Unix-socket path past the 104-byte sun_path limit). Windows CI
is unaffected. Server reconnect/replay behavior matched the contract exactly.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
The gateway emits a ReplayGap sentinel MxEvent at the head of a StreamEvents
stream resumed via after_worker_sequence when the requested cursor predates the
oldest retained event. Clients previously ignored it, silently mis-treating a
lossy resume as continuous. Each client now surfaces the sentinel as a distinct,
typed, non-terminal signal (never synthesized, never swallowed) so a consumer can
detect the gap and re-snapshot; resume contract is
after_worker_sequence = oldest_available_sequence - 1.
- .NET: MxEventStreamItem (IsReplayGap/ReplayGap/Event) via new StreamEventItemsAsync
+ AsStreamItemsAsync extension. Build clean, 87 passed.
- Go: EventResult.ReplayGap field + IsReplayGap(); ReplayGap type alias. build/vet/test clean.
- Rust: EventItem enum (Event/ReplayGap); EventStream now yields Result<EventItem, Error>;
CLI renders REPLAY_GAP line / replayGap JSON. fmt/check/test/clippy clean.
- Python: ReplayGap dataclass; stream_events yields pb.MxEvent | ReplayGap. 131 passed.
- Shared docs: ClientLibrariesDesign non-goals reframed (reconnect-replay protocol is
consumable; auto-reconnect stays a non-goal); CrossLanguageSmokeMatrix resume-gap note.
Java client is deferred to the windev batch (no local JRE); CLI-15 stays open until it lands.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
WRK-06: MxStatusProxyConverter caches the four resolved FieldInfo per status
type in a static ConcurrentDictionary (the GetField metadata scan ran 4x per
status per event on the STA path). GetValue+Convert.ToInt32 still run per event
(late-bound RCW). Exceptions byte-identical: missing-field message unchanged
(ResolveField, not cached on throw via GetOrAdd); null-value message unchanged.
WRK-11: MxAccessEventQueue.Enqueue takes ownership of the passed MxEvent -
stamps WorkerSequence/WorkerTimestamp on it in place and enqueues it, no
Clone(). Audited all 3 callers (base/alarm event sinks, provider-mode handler):
each builds a fresh event per Enqueue, none reuse it. MxAccessValueCache.Set now
deep-copies its retained Value/SourceTimestamp/Statuses so the cache snapshot
never aliases the queue-owned (later serialized) event. Net: alarm/other events
clone nothing (was full clone); data-change clones payload-only.
WRK-12: WorkerFrameWriter coalesces the flush across a drained batch - each
frame is written but not flushed individually; one FlushAsync after the batch,
then all written frames complete. Preserves the written+flushed completion
contract; a burst of N events costs 1 flush, not N. On write failure the whole
in-flight batch + queue fail so no caller hangs.
IPC-15 (doc): the multi-event WorkerEnvelope body remains unimplemented (wire
still carries one event per worker_event frame); gateway.md Performance section
now distinguishes the shipped flush-coalescing from that deferred proto change.
net48-safe (no init/records; readonly struct cache entry). Worker builds x86
only - verification on windev. Tests added: converter cache-reuse, queue
ownership-transfer, value-cache snapshot independence, writer batch-flush-once.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
TST-11: single-source the .NET-side version in src/Directory.Build.props so
Server/Worker/Contracts/tests stamp 0.1.2 (was the SDK default 1.0.0) and
InformationalVersion carries the git short SHA (0.1.2+<sha>) for support
correlation; the git query is guarded (ContinueOnError) so a build outside a
git checkout still succeeds. Verified: Server assembly stamps 0.1.2+579282f,
build clean. Kept at 0.1.2 (matches Contracts + aligned Python/Rust/Go
clients); Java leads at 0.2.0 post JDK-17 retarget. Convergence to a single
0.2.0 cadence left as a release decision, not forced here.
WRK-15 (docs-only, no x86 worker build needed): correct the STA thread name in
docs/WorkerSta.md + docs/MxAccessWorkerInstanceDesign.md to the actual
MxGateway.Worker.STA (was the FQ ZB.MOM.WW.MxGateway.Worker.STA), and fix the
stale heartbeat-counter note - CaptureHeartbeat now populates event queue depth
(eventQueue.Count) and sequence (LastEventSequence) from the live queue.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
SEC-25 (near-term hardening; full per-session EventsHub ACL stays deferred to
roadmap item 12): DashboardEventBroadcaster now redacts tag values from a deep
CLONE of the event before mirroring to SignalR when Dashboard:ShowTagValues is
false (the default). Clears MxEvent.value (covers OnDataChange/OnWriteComplete/
OperationComplete/OnBufferedDataChange — their bodies are empty discriminators,
values ride in the top-level field incl. buffered arrays) and the alarm body's
current_value/limit_value. Source event never mutated (shared with the gRPC
path + replay ring) - verified by a source-not-mutated test. Makes the formerly
dead ShowTagValues flag live for the mirror. EventsHub TODO(per-session-acl)
kept and tied to roadmap item 12. Tests: DashboardEventBroadcasterTests (3).
SEC-30: trim docs/Diagnostics.md to mark opt-in command-value logging as NOT
YET IMPLEMENTED (no LogCommandValues knob, RedactCommandValue has no call
sites, no values logged); wiring deferred pending secured-bulk redaction
coverage (SEC-13). No option/call sites added.
Server build clean (0 warnings); Dashboard tests 152/152.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Replace the replay buffer's LinkedList<ReplayEntry> (a node allocation per
retained event on the fan-out hot path) with a preallocated ReplayEntry[] ring
sized to ReplayBufferCapacity, tracked by _replayHead (oldest) + _replayCount.
Appending a retained event now allocates nothing.
Behavior preserved exactly: ascending-WorkerSequence append order; capacity
eviction (overwrite head + advance when full); time trim via
_timeProvider.GetUtcNow() cutoff at the same three call sites; oldest-read for
ReplayGap math; both replay-from-sequence query paths + highest-seen tracking;
same _replayLock. Capacity-0 stays retain-nothing (guarded early return, no
modulo-by-zero).
Server build clean (0 warnings); Distributor/Replay tests 33/33 incl. two new
cases (multi-wrap ring keeps newest in order; capacity-1 overwrite + gap).
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Behavior-preserving allocation cuts on the per-event/per-command path:
- GWC-06: StreamEvents send-timing uses Stopwatch.GetTimestamp() +
GetElapsedTime() instead of a per-event Stopwatch allocation (same measured
span).
- GWC-07/IPC-05 (event): MapEvent transfers ownership of the inner MxEvent
instead of .Clone()-ing it. Safe: WorkerEvent is parsed fresh per pipe frame
with the distributor pump as its single consumer (GWC-01), MapEvent runs once
before fan-out, and every downstream consumer (subscribers, replay ring)
only READS the event (WorkerSequence is stamped upstream; verified no
post-mapping mutation). Comment documents the invariant + restore-clone
caveat if a second consumer is added.
- IPC-05 (command): CreateCommandEnvelope no longer re-clones; MapCommand
already isolated the graph from the caller-owned gRPC message.
- GWC-15: grpc_stream_queue.depth converts from a per-event push counter to an
ObservableGauge summing registered channel sources at scrape time only
(name/semantics unchanged); removes all per-event .Count/lock work.
Kept every load-bearing isolation clone (MapCommand, Invoke, bulk filters,
MapCommandReply). Server build clean (0 warnings); EventStream/Metrics/
Distributor/Mapper tests 62/62 (incl. formerly-flaky queue-depth tests, now
green under the lazy gauge, + 2 new MapEvent ownership tests). Docs: Metrics.md,
Grpc.md.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Gateway server-side named-pipe frame path, wire format byte-identical:
- Writer (GWC-08/IPC-13): serialize once into a single ArrayPool-rented buffer
holding the 4-byte LE length prefix + payload, then one WriteAsync. Removes
the second serialization pass (ToByteArray re-ran CalculateSize), the
separate prefix array + second stream write, and per-frame heap allocation.
- Reader (IPC-14): rent the payload buffer from ArrayPool instead of new byte[]
per frame; read/parse bounded to the real length, return in finally. Matches
the worker side which already pools.
- Correctness: length tracked separately from (larger) pool capacity; every
rented buffer returned exactly once in finally incl. the malformed-protobuf
path; ParseFrom copies into the message so the envelope never aliases the
returned buffer.
Cross-checked LE-prefix framing agrees across gateway+worker writer/reader.
Server build clean (0 warnings); WorkerFrameProtocol tests 10/10 incl. a new
large-payload-near-cap round-trip (forces pool buffer > frame). The 2 failing
WorkerClient/FakeWorkerHarness tests are the known macOS UDS 104-char path
limit, not a regression.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW
Restore the __Host- browser guarantees for the default secure deployment
without breaking plaintext/dev:
- DashboardServiceCollectionExtensions PostConfigure now resolves the cookie
name as: explicit MxGateway:Dashboard:CookieName override wins; else
__Host-MxGatewayDashboard when SecurePolicy==Always (RequireHttpsCookie
true); else the plain MxGatewayDashboard default. Guard: never apply the
__Host- prefix without Secure (browsers silently drop it).
- DashboardAuthenticationDefaults: add SecureCookieName const; keep the plain
CookieName as the non-secure fallback.
- Docs corrected to the actual conditional contract (five stale claims):
gateway.md, GatewayProcessDesign.md, ImplementationPlanGateway.md,
GatewayDashboardDesign.md, CLAUDE.md; GatewayConfiguration.md phrasing
tightened.
- Test: DashboardCookieOptionsTests asserts the name flips with
RequireHttpsCookie and that an explicit override wins.
Server build clean (0 warnings); Dashboard tests 149/149.
Claude-Session: https://claude.ai/code/session_01DMXXvNuPekkkrTEyPNxEkW