From 2b74851f96ab30524d04509a519cdc768be7fe8d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 15 Aug 2026 03:13:51 -0400 Subject: [PATCH] =?UTF-8?q?docs(plans):=20residual=20remediation=20plan=20?= =?UTF-8?q?=E2=80=94=207=20remaining=20deferred=20items,=20one=20opus=20ag?= =?UTF-8?q?ent=20each?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-08-15-residual-remediation-plan.md | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 docs/plans/2026-08-15-residual-remediation-plan.md diff --git a/docs/plans/2026-08-15-residual-remediation-plan.md b/docs/plans/2026-08-15-residual-remediation-plan.md new file mode 100644 index 00000000..85cffe2c --- /dev/null +++ b/docs/plans/2026-08-15-residual-remediation-plan.md @@ -0,0 +1,140 @@ +# Residual Remediation Plan — the seven remaining deferred items + +**Date:** 2026-08-15 · **Base:** `main` @ `f228ac22` · **Execution:** one opus subagent per item, +parallel git worktrees, merged by the orchestrator in conflict-safe order, single rig +redeploy + verification pass at the end. + +Scope = the seven items left open after the arch-review remediation program closed residuals +#1 (DCL counter drift, `491df111`) and #7 (target-scale load test, `40eff637`). Sources: +residuals register in `2026-08-15-arch-review-remediation-execution-log.md` and deferred-work +register rows. + +## Execution rules (all agents) + +- Verify worktree base is `f228ac22` or a descendant before starting; branch names below. +- **Do NOT edit** `docs/plans/2026-07-08-deferred-work-register.md` or the execution-log + residuals register — 7 parallel editors of one file guarantees conflicts. Report closure + text + any **candidate new register rows** in the final report; the orchestrator records + them centrally. +- **Rig ownership:** only R6 (Playwright) may touch the `docker/` rig. R1/R2 are code+tests + only; the orchestrator does one consolidated `bash docker/deploy.sh` + live probe after all + merges. +- Component docs / CLAUDE.md updates travel with the change per repo rules. No pushes anywhere. +- Build `dotnet build ZB.MOM.WW.ScadaBridge.slnx` clean; run the tests your change touches. + +## R1 — Per-table `needs_snapshot` in LocalDb (cross-repo) — branch `localdb-per-table-snapshot` + +**Problem:** baselining one table (`RegisterReplicated(..., baselineExistingRows: true)`) sets a +global `needs_snapshot`, so enabling replication for one new table re-streams every registered +table in both directions. + +**Approach:** in `~/Desktop/scadaproj/ZB.MOM.WW.LocalDb` (branch off its `main` @ `f627cee`): +move the flag per-table (bookkeeping schema v3, upgraded in place on open, like v1→v2 was). +Snapshot phase streams only flagged tables. Wire compatibility is the hard constraint: extend +the snapshot negotiation **additively** (absent per-table scoping field ⇒ full snapshot, so +0.1.x/0.2.x peers keep syncing). Crash safety: a table's flag clears only after *that table's* +snapshot is acked. Version per library convention (schema change, wire-compatible ⇒ 0.3.0). +Update the library README + `RegisterReplicated` remarks (which name this as the follow-up). +Publish to the Gitea feed via `build/push.sh` only after the library suite is green. Then, in +the ScadaBridge worktree: bump `Directory.Packages.props`, build, run the LocalDb-touching +suites, update the CLAUDE.md LocalDb bullet + the umbrella `~/Desktop/scadaproj/CLAUDE.md`. + +**Verify:** two-node integration test proving single-table baseline streams only that table; +mixed-version interop test (old peer ⇒ full snapshot); ScadaBridge suite green on the bump. + +## R2 — gRPC event batching — branch `grpc-event-batching` + +**Problem:** every `AttributeValueChanged`/`AlarmStateChanged` rides one gRPC message; at target +scale that is 37.5k messages/s/site of per-message overhead. + +**Approach:** additive wire shape in the vendored proto (`Communication/SiteStreamGrpc/`, +manual toggle-build-copy-untoggle regeneration; field numbers never reused): a batch message +(repeated events) exposed via a **new** RPC or additive field so old peers are untouched — +new-central↔old-site and old-central↔new-site must both keep working on the existing path. +Server coalesces per subscriber under a small bounded window (size + time cap, e.g. ≤100 +events / ≤25 ms, configurable via options, validated); client unpacks and feeds the existing +per-event pipeline so timestamps, generation fencing, reconnect-on-graceful-completion, and +`IsLive` semantics are untouched. Batch-size histogram metric. Note interplay with register +row 31: batching sits downstream of the shared publish stage and does not change the burst +ceiling — say so in the component doc. + +**Verify:** batcher unit tests (window/cap/flush-on-close), client/server round-trip +integration, version-skew tests both directions, existing Communication suites green. +No rig redeploy (orchestrator does it post-merge). + +## R3 — Deployments page server-side paging + status counts — branch `deployments-server-paging` + +**Problem:** the Central UI Deployments page client-materializes the full deployment list. + +**Approach:** find the page's data path first; add a paged repository query (EF, following the +repo's existing paged patterns — Audit Log keyset paging is the reference) plus a status-count +aggregation query (counts by status computed server-side, one grouped query). Wire through +whatever service layer the page uses; UI gets paging (pager or `Virtualize`, matching an +existing page's idiom), 500 ms filter debounce, and status-count tiles. Bootstrap only, no +third-party components; match the corporate UI conventions. + +**Verify:** repository paging + count tests; existing CentralUI/ConfigurationDatabase suites +green; page behavior exercised via whatever bUnit/unit seam exists for sibling pages. + +## R4 — OtOpcUa LocalDb pin bump — branch (in `~/Desktop/OtOpcUa`) `localdb-bump` + +**Problem:** OtOpcUa pins LocalDb 0.1.3 — supported skew, but drifting further as LocalDb moves. + +**Approach:** bump to the newest LocalDb on the Gitea feed (0.2.1 now; if R1's 0.3.0 lands on +the feed before you finish, take that instead). Reconcile API deltas (0.2.x added +`DeregisterReplicated`, `MaxBatchBytes`, `baselineExistingRows`; check what OtOpcUa's usage +touches). Build + full OtOpcUa test suite. Update its own docs/CLAUDE.md where the pin is +recorded, and the umbrella `~/Desktop/scadaproj/CLAUDE.md` if it records the skew. + +**Verify:** OtOpcUa builds clean, full suite green. No push. + +## R5 — SandboxTests timing pin + flake-pattern sweep — branch `sandbox-timing-fix` + +**Problem:** a fragile timing pin in the SandboxTests (noted pre-existing during the +remediation program); the AwaitAssert-then-bare-assert flake class it belongs to had 3 other +instances root-caused and fixed (`c4caebe9`, `cfa6acbf`). + +**Approach:** locate the pin, root-cause it causally (delay-injection reproduction + negative +control, same discipline as the prior flake fixes), replace the timing assumption with +deterministic synchronization — test-side only unless the product is genuinely wrong. Then do +the deferred **bounded sweep**: grep the test suites for the same pattern (AwaitAssert on one +observable followed by a bare assert on a strictly-later observable), fix what's clearly the +same class, list anything ambiguous as candidate register rows instead of guessing. + +**Verify:** fixed tests re-run ≥5× green; touched suites green; fail-before-fix demonstrated +via injected delay. + +## R6 — Playwright environment failures — branch `playwright-env-fixes` (owns the rig) + +**Problem:** 14 Playwright failures present on `main` before the remediation branch, +rig-state related, never triaged. + +**Approach:** run the suite against the current rig for a fresh baseline; catalog and classify +every failure (stale rig data assumption, timing, selector drift, genuine app bug). Fix causes +at the right layer — test fixes, or rig seed-state fixes via the ScadaBridge CLI (preferred +over DB edits). Rig redeploy allowed for this agent if needed. Anything irreducible or +app-side gets a precise writeup as a candidate register row rather than a papered-over test. + +**Verify:** clean full-suite re-run; report before/after failure counts with per-test root +causes. A genuine app bug found here is a finding to report, not necessarily to fix. + +## R7 — `site_events` purge oplog-visibility operator note — branch `site-events-purge-note` + +**Problem:** WP3.2's sliced retention DELETE is CDC-captured on a replicated site (correct by +design — no separate resync path), so a purge burst shows up in `LocalDbOplogBacklog`; an +operator watching the gauge can misread it as a replication problem. + +**Approach:** documentation + one log line, no behavior change: a note in +`docs/deployment/topology-guide.md` and `Component-SiteEventLogging.md` (what the burst looks +like, why it is expected, how it drains), plus an Information-level log when a purge on a +replication-enabled node deletes rows ("N rows purged; transient oplog backlog expected"). + +**Verify:** build green; the purge path's existing tests still green; log line unit-covered if +the purge path has a test seam. + +## Merge & close-out (orchestrator) + +Merge order: R5 → R7 → R4 (separate repo, no merge here) → R3 → R2 → R1 → R6. Then: record +all closures + candidate rows in the registers, consolidated `bash docker/deploy.sh` + live +probe (stream batching live check, replication convergence, site_events purge log line), +full-solution build + test, hold push for the user.