From ddb382c137995e6dbbcbf84a22369246cb9f2828 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 7 Aug 2026 05:25:42 -0400 Subject: [PATCH] fix(TST-29): retire oldtasks.md; delete root docs-review artifacts Migrate the durable session-resilience governance record (Phase 5 orphan-worker reattach deferred-not-planned, EnableOrphanReattach does not yet exist, settled Phase-4 Viewer-default decision) from oldtasks.md into a new "Session-Resilience Epic Scope" entry in docs/DesignDecisions.md, repoint CLAUDE.md and stillpending.md's oldtasks.md references to the new home / tasks.json, and git rm oldtasks.md now that it has no unique content left. Flip TST-29 to Done in the archreview tracking registers. The five untracked root docs-review artifacts (MxAccessGateway-docs-*, MxGatewayClient-docs-*) are absent from this worktree; they must be deleted from the main working tree separately (gitignored, no repo impact). --- CLAUDE.md | 2 +- .../2026-07-12/remediation/00-tracking.md | 3 +- .../remediation/60-testing-docs-gaps.md | 2 +- docs/DesignDecisions.md | 30 ++++++ oldtasks.md | 95 ------------------- stillpending.md | 4 +- 6 files changed, 36 insertions(+), 100 deletions(-) delete mode 100644 oldtasks.md diff --git a/CLAUDE.md b/CLAUDE.md index 12fef36..205accb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -112,7 +112,7 @@ powershell -ExecutionPolicy Bypass -File scripts/run-client-e2e-tests.ps1 - **Style guides** in `docs/style-guides/` are authoritative. Follow `CSharpStyleGuide.md` for gateway/worker/.NET-client code: file-scoped namespaces, `sealed` by default, `Async` suffix on Task-returning methods, MXAccess-aligned names (`MxStatusProxy`, `ServerHandle`, `ItemHandle`, `HResult`). - **MXAccess parity is the contract.** Don't "fix" surprising MXAccess behavior (e.g., `WriteSecured` failing before a value-bearing NMX body, distinct `OperationComplete` semantics, invalid-handle exceptions) unless the client explicitly opts into a non-parity mode. The installed MXAccess COM component is the baseline. - **Don't synthesize events.** The gateway forwards only events the worker emits; it never invents `OperationComplete` from write completion or command replies. -- **One worker per session** (invariant). Multi-subscriber event fan-out and reconnect-with-replay have shipped and are config-gated: `AllowMultipleEventSubscribers` (default `false`) enables fan-out up to `MaxEventSubscribersPerSession` (default `8`); `DetachGraceSeconds` (default `30`) retains a session after its last subscriber drops so clients can reconnect; `ReplayBufferCapacity` / `ReplayRetentionSeconds` control how much event history the replay ring keeps. Default config is single-subscriber (`AllowMultipleEventSubscribers` off), but detach-grace and replay retention are **on** by default (`DetachGraceSeconds=30`, `ReplayBufferCapacity=1024`, `ReplayRetentionSeconds=300`): a detached session is retained for 30 s and recent events are buffered for reconnect. The reconnect protocol is consumable end-to-end: a resuming `StreamEvents` (via `after_worker_sequence`) that predates the retained ring gets a `ReplayGap` sentinel, and all five official clients surface it as a typed signal. Orphan-worker reattach after a gateway restart is **deferred, not planned** — see `oldtasks.md` (session-resilience epic Phase 5); the invariant on the next line stands. See `docs/DesignDecisions.md` and `docs/Sessions.md`. +- **One worker per session** (invariant). Multi-subscriber event fan-out and reconnect-with-replay have shipped and are config-gated: `AllowMultipleEventSubscribers` (default `false`) enables fan-out up to `MaxEventSubscribersPerSession` (default `8`); `DetachGraceSeconds` (default `30`) retains a session after its last subscriber drops so clients can reconnect; `ReplayBufferCapacity` / `ReplayRetentionSeconds` control how much event history the replay ring keeps. Default config is single-subscriber (`AllowMultipleEventSubscribers` off), but detach-grace and replay retention are **on** by default (`DetachGraceSeconds=30`, `ReplayBufferCapacity=1024`, `ReplayRetentionSeconds=300`): a detached session is retained for 30 s and recent events are buffered for reconnect. The reconnect protocol is consumable end-to-end: a resuming `StreamEvents` (via `after_worker_sequence`) that predates the retained ring gets a `ReplayGap` sentinel, and all five official clients surface it as a typed signal. Orphan-worker reattach after a gateway restart is **deferred, not planned** — see `docs/DesignDecisions.md` (Session-Resilience Epic Scope, session-resilience epic Phase 5); the invariant on the next line stands. See `docs/DesignDecisions.md` and `docs/Sessions.md`. - **Gateway restart does not reattach orphan workers.** The first version terminates orphaned workers on startup; do not design code paths that assume reattachment. - **No Blazor UI component libraries.** Dashboard uses local Bootstrap CSS/JS only — do not introduce MudBlazor, Radzen, FluentUI, etc. - **Don't log secrets or full tag values by default.** API keys, passwords, `WriteSecured` payloads, and `AuthenticateUser` credentials must never reach logs. Value logging is opt-in and redacted. diff --git a/archreview/2026-07-12/remediation/00-tracking.md b/archreview/2026-07-12/remediation/00-tracking.md index 73bdcb8..cb94a4b 100644 --- a/archreview/2026-07-12/remediation/00-tracking.md +++ b/archreview/2026-07-12/remediation/00-tracking.md @@ -130,7 +130,7 @@ Full design + implementation for each row lives in the linked domain doc under i | TST-26 | Medium | P1 | S | TST-25 (same commit) | Done | Docs/scripts describe removed CI jobs; Generated/-guard reattributed to check-codegen | | TST-27 | Medium | P1 | S | — | Not started | `ShowTagValues` config row still says "Reserved" after SEC-25 made the flag live | | TST-28 | Low | P2 | S | relates IPC-02 (old) | Not started | Gateway-side `max_frame_bytes` handshake untested in the CI-run suite | -| TST-29 | Low | P2 | S | — | Not started | Retire `oldtasks.md` (fold Phase-5 governance into DesignDecisions.md); delete root artifacts | +| TST-29 | Low | P2 | S | — | Done | Retire `oldtasks.md` (fold Phase-5 governance into DesignDecisions.md); delete root artifacts | | TST-30 | Low | P2 | M | — | Not started | Single shared Gitea runner is a CI throughput/availability bottleneck (cross-repo contention, no run cancel/delete API) | ## Cross-cutting clusters @@ -161,3 +161,4 @@ Sequence these together rather than piecemeal — several are one change set spa | 2026-07-13 | Operator bring-up complete: dedicated CI ed25519 key installed in windev `administrators_authorized_keys` (authorized into `dohertj2`, which owns the working MXAccess/toolchain env — a fresh OS account would break the build; the key is independently revocable), Gitea secrets `WINDEV_SSH_KEY`/`WINDEV_SSH_KNOWN_HOSTS` + variable `WINDEV_SSH_USER=dohertj2` stored, runner→`10.100.0.48:22` egress verified on the `traefik` net, issue-write confirmed. **TST-25/TST-26 → `Done`:** credentialed `windows-x86` ran GREEN on `d769244` (Gitea run #37) — Linux runner SSHed windev, checked out the SHA in `C:\build\mxaccessgw-ci` under lock, ran the x86 Worker build + `Worker.Tests`, exit 0; `nightly-windev` correctly skipped on the push event. Branch merged to `main`. Follow-ups (old tracker): revisit **TST-05** (scheduled live smoke — now covered by `nightly-windev`) and **TST-24** (client wire tests) which this unlocks. | | 2026-07-13 | Ran the TST-25 acceptance checks (scripts/ci/README.md) — they caught **two real CI defects, both fixed** on `fix/tst-25-ci-key-log-leak`: (1) **CI SSH key leaked in cleartext** in the `windows-x86` step env echo (Gitea's line-oriented masker missed the multiline PEM) — rotated the CI key on windev (old pubkey revoked), stored the key **base64-encoded** so the masker redacts it to `***` (confirmed on run #38), taught `run-windev-ci.sh` to decode, dropped the redundant public known-hosts secret from the job env; (2) **bootstrap lock race** — `run-windev-ci.sh`'s pre-hand-off `git fetch`/`checkout` ran outside the worktree lock, so concurrent runs collided on `.git/index.lock`; the bootstrap now holds the lock (ps1 re-uses it via `MXGW_CI_LOCK_HELD`), retest confirmed clean serialization. Also **deflaked** `SessionManagerTests` fail-fast timing assertions (absolute `<100ms` wall-clock bound flaked under CI load; now anchored to the configured timeout / dropped for the zero-timeout case). Checks passed: unreachable-host fast-fail (exit 255/15s), deliberate-red propagation (Worker.Tests failure → exit 1), lock concurrency (2nd run waits), no-key-in-logs (masked). Merge target `df7e20d` verified GREEN via the local windev path (Worker build + 356 tests); merged to `main` `19cbf7b`. Check 6 (forced-failure nightly issue): issue endpoint+token proven live at bring-up (#124); in-CI forced-failure probe abandoned to shared-runner congestion (residual `if: failure()` gating is standard Actions). | | 2026-07-13 | New finding **TST-30** (`Low`/`P2`) added — surfaced during TST-25 acceptance: CI runs on a single shared `gitea-runner` (`maxParallel=1`, co-located `10.100.0.35`) interleaved with `dohertj2/lmxopcua`, and Gitea 1.26 exposes no run cancel/delete, 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` bypass. Roll-ups updated (Testing Low 2→3, total 47→48; P2 9→10). | +| 2026-08-07 | **TST-29 → `Done`:** migrated the Phase-5 (orphan-worker reattach) deferred-not-planned governance record and the settled Phase-4 Viewer-default decision from `oldtasks.md` into a new "Session-Resilience Epic Scope" entry in `docs/DesignDecisions.md`; repointed CLAUDE.md and `stillpending.md:7,165` from `oldtasks.md` to `docs/DesignDecisions.md` / `docs/plans/2026-06-15-session-resilience.md.tasks.json`; `git rm oldtasks.md`. The five untracked root docs-review artifacts (`MxAccessGateway-docs-{issues,fixed,final}.md`, `MxGatewayClient-docs-{issues,fixed}.md`) were absent from this worktree — delete from the main working tree separately. | diff --git a/archreview/2026-07-12/remediation/60-testing-docs-gaps.md b/archreview/2026-07-12/remediation/60-testing-docs-gaps.md index 01fc448..3f47234 100644 --- a/archreview/2026-07-12/remediation/60-testing-docs-gaps.md +++ b/archreview/2026-07-12/remediation/60-testing-docs-gaps.md @@ -14,7 +14,7 @@ Prior-cycle open findings (TST-05..24 where still open) are tracked in the prior | TST-26 | Medium | P1 (folded into TST-25) | S | TST-25 | Done | docs/GatewayTesting.md, check-codegen.ps1, and ci.yml comments describe removed CI jobs | | TST-27 | Medium | P1 (doc batch) | S | — | Not started | `ShowTagValues` config row still says "Reserved" after SEC-25 made the flag live | | TST-28 | Low | P2 | S | relates IPC-02 | Not started | Gateway-side `max_frame_bytes` handshake field untested in the CI-run suite | -| TST-29 | Low | P2 | S | — | Not started | Retire `oldtasks.md` after folding the Phase-5 governance record into DesignDecisions.md; delete root docs-review artifacts | +| TST-29 | Low | P2 | S | — | Done | Retire `oldtasks.md` after folding the Phase-5 governance record into DesignDecisions.md; delete root docs-review artifacts | | TST-30 | Low | P2 | M | — | Not started | Single shared Gitea runner is a CI throughput/availability bottleneck (cross-repo contention, no run cancel/delete) | --- diff --git a/docs/DesignDecisions.md b/docs/DesignDecisions.md index 93d4896..ecb6169 100644 --- a/docs/DesignDecisions.md +++ b/docs/DesignDecisions.md @@ -135,6 +135,36 @@ alarm state is gateway-wide, not session-scoped — every client wants the same current set plus updates, and forcing each to own a worker would multiply AVEVA polling load for no benefit. +## Session-Resilience Epic Scope + +Decision (2026-07-09, archreview TST-04; migrated here 2026-08-07 from the retired +`oldtasks.md` mirror per TST-29): the session-resilience epic +(`docs/plans/2026-06-15-session-resilience.md`, 28 tasks) resolves into three per-phase +decisions rather than one open backlog. + +- **Phase 3 (reconnect)** — essentially complete. Task 13 (owner re-validation) shipped + as archreview **TST-02** (P0, session attach is owner-scoped; see + [Session Reconnect](#session-reconnect) above). Task 15 (reconnect integration test) + shipped as **TST-01** (`GatewayEndToEndReconnectReplayTests`). Task 14 (client + `ReplayGap` handling) shipped as **CLI-15** for four of five clients + (.NET/Go/Rust/Python); the Java client is the only remainder. +- **Phase 4 (per-session dashboard ACL)** — scoped, not yet built. Tracked as archreview + **TST-15**. The Viewer-default decision is settled: admin-sees-all, Viewer strictly + scoped to sessions it owns or is granted — matching the gRPC owner-binding decision in + [Session Reconnect](#session-reconnect) above, for consistency between the gRPC and + dashboard surfaces. +- **Phase 5 (orphan-worker reattach)** — deferred, not planned. It would reverse the + "Gateway restart does not reattach orphan workers" invariant (see CLAUDE.md), adding a + stable gateway-instance id, an adoption-manifest SQLite store, a worker phone-home + reconnect protocol, and gateway-side adoption (re-open pipes, nonce-validate, reject + impostors). It stays deferred unless a concrete requirement appears; the invariant + stands. **`EnableOrphanReattach` does not exist and must not be referenced anywhere as + if it does** until that task actually lands. + +`docs/plans/2026-06-15-session-resilience.md.tasks.json` remains the sole resume state +for the still-pending Phase 4 tasks (16-19) and the deferred Phase 5 tasks (20-28) — one +authority, no mirror. + ## Authentication Decision: API key authentication for the public gateway. diff --git a/oldtasks.md b/oldtasks.md deleted file mode 100644 index 16fa7f7..0000000 --- a/oldtasks.md +++ /dev/null @@ -1,95 +0,0 @@ -# Saved Task List — Session Resilience Epic - -> Snapshot taken 2026-06-16, before switching to the dashboard disable-login feature. -> This is the in-flight epic from `docs/plans/2026-06-15-session-resilience.md`. - -## How to resume - -``` -/superpowers-extended-cc:executing-plans docs/plans/2026-06-15-session-resilience.md -``` - -The authoritative resume state lives in -`docs/plans/2026-06-15-session-resilience.md.tasks.json` (tasks 1–12 completed, -13–28 pending). This file is just a human-readable mirror. - -## Status - -**Governance update 2026-07-09 (archreview TST-04).** The epic is resolved into three -decisions rather than one open backlog: - -- **Phase 3 (reconnect) — finishing now, essentially complete.** Task 13 (owner - re-validation) shipped as archreview **TST-02** (P0, session attach is owner-scoped, - see CLAUDE.md Authentication). Task 15 (reconnect integration test) shipped as - **TST-01** (`GatewayEndToEndReconnectReplayTests`). Task 14 (client `ReplayGap` - handling) shipped as **CLI-15** for four of five clients (.NET/Go/Rust/Python); - the Java client is the only remainder, batched to the windev build host. -- **Phase 4 (per-session dashboard ACL) — scoped, not yet built.** Tracked as archreview - **TST-15**. The previously-open Viewer-default decision is **settled**: admin-sees-all, - Viewer strictly scoped to sessions it owns/is granted — matching TST-02's gRPC owner - binding for consistency. -- **Phase 5 (orphan-worker reattach) — DEFERRED, not planned.** It reverses the CLAUDE.md - invariant "Gateway restart does not reattach orphan workers" and adds an adoption manifest - store + worker phone-home protocol. It stays deferred unless a concrete requirement - appears. **The `EnableOrphanReattach` flag (Task 26) does not exist and must not be - referenced anywhere as if it does** until that task actually lands. - -Original snapshot (historical): **12 of 28 tasks complete** (Phases 1–2 + reconnect core of -Phase 3), merged to `main` (commit `c446bef`). - -### Completed — Phase 1 (Foundation) -- ✅ Task 1 (#108): Add OwnerKeyId to the session -- ✅ Task 2 (#109): SessionEventDistributor skeleton -- ✅ Task 3 (#110): Bounded replay ring buffer -- ✅ Task 4 (#111): Rewire AttachEventSubscriber + EventStreamService onto distributor -- ✅ Task 5 (#112): Per-subscriber backpressure isolation -- ✅ Task 6 (#113): Dashboard broadcaster becomes a distributor subscriber - -### Completed — Phase 2 (Multi-subscriber fan-out) -- ✅ Task 7 (#114): Remove validator block + add subscriber cap option -- ✅ Task 8 (#115): Subscriber-lease collection + cap enforcement -- ✅ Task 9 (#116): Multi-subscriber end-to-end test (FakeWorkerHarness) - -### Completed — Phase 3 (Reconnect core) -- ✅ Task 10 (#117): Proto — ReplayGap signal -- ✅ Task 11 (#118): Detach-grace session retention -- ✅ Task 12 (#119): Replay-on-reconnect + emit ReplayGap - -### Phase 3 finish — DONE (via archreview P0/P2) -- ✅ Task 13 (#120): Owner re-validation on reconnect — shipped as **TST-02** (P0). -- 🔄 Task 14 (#121): Client ReplayGap handling — shipped as **CLI-15** for 4/5 clients - (.NET/Go/Rust/Python); Java pending (windev batch). Per-language presence-check idiom - for `optional` message fields carried in each client's surface. -- ✅ Task 15 (#122): Reconnect integration test (fake worker) — shipped as **TST-01**. - -### Phase 4 (Per-session dashboard ACL) — SCOPED, tracked as archreview TST-15 -- ⏳ Task 16 (#123): gRPC session-owner gate + all-sessions admin scope — blockedBy 9, 1 - - Note: the gRPC owner gate itself already exists (TST-02); Phase 4 adds the admin - all-sessions scope + the dashboard-side twin. -- ⏳ Task 17 (#124): Session Tag + dashboard group-to-tag config — blockedBy 9 -- ⏳ Task 18 (#125): EventsHub per-session ACL + hub-token tag claim — blockedBy 17 - - Decision SETTLED: admin-sees-all, Viewer strictly scoped to owned/granted sessions - (matches TST-02 gRPC owner binding). -- ⏳ Task 19 (#126): ACL tests incl. live LDAP users — blockedBy 18 - -### Phase 5 (Orphan-worker reattach) — DEFERRED, NOT PLANNED -Deferred unless a concrete requirement appears. It reverses the CLAUDE.md invariant -"Gateway restart does not reattach orphan workers" and adds an adoption manifest store + -worker phone-home protocol. **`EnableOrphanReattach` (Task 26) does not exist** — do not -reference it as if it does until the task lands. -- 🚫 Task 20 (#127): Stable gateway-instance id + stable pipe naming -- 🚫 Task 21 (#128): Adoption manifest store (SQLite) -- 🚫 Task 22 (#129): Proto — worker adopt/reconnect frame -- 🚫 Task 23 (#130): Worker phone-home reconnect loop + self-terminate (net48/x86, windev) -- 🚫 Task 24 (#131): Gateway adoption — re-open pipes, nonce-validate, reject impostors -- 🚫 Task 25 (#132): Resync adopted worker + ReplayGap to subscribers -- 🚫 Task 26 (#133): EnableOrphanReattach flag (default off) + terminator fallback -- 🚫 Task 27 (#134): Gateway-restart reattach round-trip (WINDEV + live worker) -- 🚫 Task 28 (#135): Documented-rule reversals + stillpending refresh - -## Notes -- Phase 5 was designed to reverse the "Gateway restart does not reattach orphan workers" - rule (CLAUDE.md), but is now **deferred, not planned** (TST-04) — the invariant stands. -- Two deferred follow-ups noted earlier: dashboard visibility of `DetachedAtUtc` on - `DashboardSessionSummary`. -- Worker (net48/x86) tasks build/test on windev; everything else builds on macOS. diff --git a/stillpending.md b/stillpending.md index 1f51e91..929ccf8 100644 --- a/stillpending.md +++ b/stillpending.md @@ -4,7 +4,7 @@ > **Resolution update (2026-06-15, branch `feat/stillpending-completion`):** The actionable items were implemented and verified per `docs/plans/2026-06-15-stillpending-completion.md`. **§1.1** (all 11 worker command kinds), **§1.2** (audit CorrelationId), and the **§4** client CLI/helper parity gaps are **Resolved** — see per-item annotations below. Worker COM commands are live-verified on the dev rig (`efd9971`, `f7ada90`). Remaining open items are the documented residuals (**§1.3**, **§1.4**, the **§3** vendor/capture-gated questions incl. the new **§3.2** multi-sample buffered residual) and the deliberate v1 scope of **§2**. Zero `.proto` changes were needed (all reply messages already existed). > -> **Resolution update (2026-06-16, `main`):** The **session-resilience epic** (`docs/plans/2026-06-15-session-resilience.md`) landed its first **12 of 28 tasks** on `main` (through merge `c446bef`), which moves several **§2** items off "deferred": **multi-subscriber fan-out is now Resolved**, and **reconnectable sessions** has its server-side core (detach-grace window + replay-on-reconnect + a new `ReplayGap` signal on `MxEvent`). Still pending in that epic: reconnect Tasks 13–15 (owner re-validation, client `ReplayGap` handling, integration test), **Phase 4** per-session dashboard ACL (Tasks 16–19 — the §2/§7.6/§8 EventsHub-ACL item), and **Phase 5** orphan-worker reattach (Tasks 20–28). Separately, a `MxGateway:Dashboard:DisableLogin` dev flag shipped (`ca443b1`) — auto-authenticates the dashboard as a multi-role admin; default off, **enabled on the 10.100.0.48 deployment**. Per-item §2 status annotated below; remaining epic tasks tracked in `oldtasks.md`. +> **Resolution update (2026-06-16, `main`):** The **session-resilience epic** (`docs/plans/2026-06-15-session-resilience.md`) landed its first **12 of 28 tasks** on `main` (through merge `c446bef`), which moves several **§2** items off "deferred": **multi-subscriber fan-out is now Resolved**, and **reconnectable sessions** has its server-side core (detach-grace window + replay-on-reconnect + a new `ReplayGap` signal on `MxEvent`). Still pending in that epic: reconnect Tasks 13–15 (owner re-validation, client `ReplayGap` handling, integration test), **Phase 4** per-session dashboard ACL (Tasks 16–19 — the §2/§7.6/§8 EventsHub-ACL item), and **Phase 5** orphan-worker reattach (Tasks 20–28). Separately, a `MxGateway:Dashboard:DisableLogin` dev flag shipped (`ca443b1`) — auto-authenticates the dashboard as a multi-role admin; default off, **enabled on the 10.100.0.48 deployment**. Per-item §2 status annotated below; remaining epic tasks tracked in `docs/plans/2026-06-15-session-resilience.md.tasks.json`. ## How to read this @@ -162,6 +162,6 @@ No placeholder/empty/`Assert.True(true)` tests were found anywhere. - **§1.4 / §3.4 / §3.5** — the AVEVA 8-arg `AlarmAckByName` is a vendor stub (−55) and `AlarmAckByGUID` is `E_NOTIMPL`; the `domain`/`full_name` fields stay forward-compat-only until AVEVA implements them. - **§3.2** — buffered commands work and the empty bootstrap converts cleanly live, but a multi-sample buffered batch is undrivable on the rig (unit-tested only). - **§3.1 / §3.3 / §3.6 / §3.7** — await live MXAccess captures. -- **§2** — mostly deliberate v1 scope, but the session-resilience epic (12/28 tasks merged to `main`) has since **resolved multi-subscriber fan-out** and landed the **reconnect server-side core**; reconnect Tasks 13–15, per-session ACL (Phase 4), and orphan-worker reattach (Phase 5) remain (see `docs/plans/2026-06-15-session-resilience.md`, `oldtasks.md`). **§5** — opt-in verification gates. **§7.6** — accepted `Won't Fix` review findings. +- **§2** — mostly deliberate v1 scope, but the session-resilience epic (12/28 tasks merged to `main`) has since **resolved multi-subscriber fan-out** and landed the **reconnect server-side core**; reconnect Tasks 13–15, per-session ACL (Phase 4), and orphan-worker reattach (Phase 5) remain (see `docs/plans/2026-06-15-session-resilience.md`, `docs/plans/2026-06-15-session-resilience.md.tasks.json`). **§5** — opt-in verification gates. **§7.6** — accepted `Won't Fix` review findings. MXAccess **event/data/value/write** mapping, the **Galaxy** RPC surface, and now the **full command surface** are complete; no `NotImplementedException`s, stubbed RPC bodies, or empty tests remain in the production paths.