0aebffe800
The 5 Driver.Historian.Wonderware* husks are untracked bin/obj build artifacts (0 tracked files; absent from slnx). A fresh worktree carries no untracked files, so there is nothing to rm. Only tracked Wonderware residue is the intentional retired doc stub + closed-review findings.md (kept). Deviation recorded in plan.
564 lines
36 KiB
Markdown
564 lines
36 KiB
Markdown
# Design + Implementation Plan — R2-12 Repo Hygiene Batch
|
||
|
||
> **Status (2026-07-12):** PLANNED — nothing executed. This plan documents commands only; the
|
||
> destructive steps (git rm of DLLs / planning files / workflow, rm -rf of husk dirs) **must not run
|
||
> until their zero-reference proof step passes AND the maintainer signs off** (flagged per task).
|
||
|
||
- **Source report:** `archreview/07-client-tooling-engineering.md` (2026-07-12 re-review; working-tree version)
|
||
- **Overall context:** `archreview/00-OVERALL.md` — prioritized action item **#12** ("Hygiene: delete `lib/` AVEVA DLLs + Wonderware husks, untrack planning files, document 5 CLI commands, E2E workflow or delete it, dedupe CI integration matrix" — Source 07, Effort Small, Impact Medium — license risk + doc trust)
|
||
- **Round-1 plan:** `archreview/plans/07-client-tooling-engineering-plan.md` (Tier B/C designs carried forward here)
|
||
- **Verified against tree at:** master `f6eaa267` (clean tracked tree; archreview/*.md working-tree updates only)
|
||
- **Scope:** `lib/`, retired `Driver.Historian.Wonderware*` disk husks, 5 root planning files, `docs/Client.CLI.md`, `.github/workflows/v2-e2e.yml` + `v2-ci.yml`, `StyleGuide.md` + `.editorconfig`, the 3 xunit 2.9.2 holdouts. Items: **07/U-5, 07/U-6, 07/U-4, 07/U-3, 07/S-3, 07/P-5 (new), 07/C-5 (StyleGuide + editorconfig; the R2-12 brief labels this C-6), 07/C-4**. 07/U-9 is already done (see below).
|
||
|
||
## Verification summary
|
||
|
||
Every item was re-verified directly against the tree at `f6eaa267` on 2026-07-12. **All items
|
||
CONFIRMED still present except U-9 (already fixed in the working tree).** Exact results:
|
||
|
||
- **U-5 CONFIRMED** — `git ls-files lib/` returns exactly 7 tracked binaries:
|
||
`lib/ArchestrA.CloudHistorian.Contract.dll`, `lib/ArchestrA.MXAccess.dll`, `lib/Historian.CBE.dll`,
|
||
`lib/Historian.DPAPI.dll`, `lib/aahClient.dll`, `lib/aahClientCommon.dll`, `lib/aahClientManaged.dll`.
|
||
A per-filename grep of every `*.csproj`/`*.props`/`*.targets`/`*.slnx` in the repo returns **zero
|
||
hits for all 7 names**, and there is **not a single `<HintPath>` element anywhere in the repo**
|
||
(`grep -rn "HintPath" --include='*.csproj' --include='*.props' --include='*.targets' .` → empty).
|
||
Zero references, proven.
|
||
- **U-6 CONFIRMED** — 5 retired directories exist on disk:
|
||
`src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware{,.Client,.Client.Contracts}` and
|
||
`tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware{.Tests,.Client.Tests}`. Each contains
|
||
**only `bin/` + `obj/`** (no source, no csproj). `git ls-files` over all 5 paths returns **0 files**
|
||
(they are untracked, gitignored bin/obj husks). `grep -i wonderware ZB.MOM.WW.OtOpcUa.slnx` → no
|
||
match (exit 1); zero `Historian.Wonderware` hits in any csproj. Tracked Wonderware residue is only
|
||
the intentional retired stub `docs/drivers/Historian.Wonderware.md` (keep — CLAUDE.md points at it)
|
||
and 3 `code-reviews/Driver.Historian.Wonderware*/findings.md` (keep — closed-review history).
|
||
- **U-4 CONFIRMED** — `git ls-files` shows all 5 tracked at root: `pending.md` (71,575 B, "As of
|
||
2026-06-16"), `current.md` (8,040 B, "Updated 2026-06-13"), `looseends.md` (2,817 B, 2026-05-18),
|
||
`stillpending.md` (22,491 B, reconciled 2026-06-18), `HISTORIAN-GATEWAY-INTEGRATION-ISSUES.md`
|
||
(11,528 B, dated 2026-06-27). `pending.md` line 3 still carries the hard rule "never stage
|
||
`pending.md` / `current.md`". `.gitignore` has **no** entry for any of them (nor for `lib/`).
|
||
One tracked cross-reference exists: `docs/Historian.md:397-399` cites `stillpending.md` by name.
|
||
- **U-3 CONFIRMED (count corrected)** — the CLI has **13** commands, not the report's 14:
|
||
`[Command("…")]` attributes in `src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/Commands/` are exactly
|
||
`ack, alarms, browse, confirm, connect, disable, enable, historyread, read, redundancy, shelve,
|
||
subscribe, write` (13 files, 13 attributes). `docs/Client.CLI.md` has `###` sections for 8:
|
||
connect, read, write, browse, subscribe, historyread, alarms, redundancy. **Missing: `ack`,
|
||
`confirm`, `shelve`, `enable`, `disable`** — the full operator alarm workflow. Options verified
|
||
from source (see item 4).
|
||
- **S-3 CONFIRMED** — `grep -rn 'Category.*E2E' tests/ --include='*.cs'` → zero matches (exit 1).
|
||
`v2-e2e.yml` header (lines 6-10) itself admits "this workflow is a green no-op". It also boots the
|
||
docker-dev fleet on a hosted runner — a fleet that per CLAUDE.md binds the **shared GLAuth at
|
||
`10.100.0.35:3893`** (LAN-only) and builds locally-defined images, so it is not
|
||
hosted-runner-bootable even if E2E tests existed.
|
||
- **P-5 CONFIRMED** — `v2-ci.yml:77-106`: the `integration` matrix re-restores, re-builds, and
|
||
re-runs `Host.IntegrationTests` + `OpcUaServer.IntegrationTests` under the **identical filter**
|
||
(`Category!=E2E&Category!=LiveIntegration`) the whole-solution `unit-tests` leg (lines 58-62)
|
||
already applies to both projects, fixtureless both times, with a report-only (`MIN_EXECUTED=0`)
|
||
gate whose tally the unit leg's trx already contains. Fully redundant.
|
||
- **C-5 CONFIRMED (path corrected)** — the file is at repo root **`StyleGuide.md`** (not
|
||
`docs/StyleGuide.md` as the report cites); line 3 reads "…for all ScadaBridge documentation".
|
||
No `.editorconfig` exists anywhere (`git ls-files '*.editorconfig'` → empty).
|
||
- **C-4 CONFIRMED (with a load-bearing wrinkle)** — `Directory.Packages.props:108` pins
|
||
`xunit` 2.9.2; exactly 3 csprojs reference it (`AdminUI.Tests`, `ControlPlane.Tests`,
|
||
`Runtime.Tests`; 44 projects are on `xunit.v3` 1.1.0). **All three holdout csprojs carry an
|
||
explicit comment: `Akka.TestKit.Xunit2` ties them to xunit v2 ("TestKit isn't ported to v3
|
||
yet").** Akka is pinned at 1.5.62. So this is an upstream-blocked migration, not a mechanical
|
||
version alignment — see item 8 for the two-branch plan.
|
||
- **U-9 ALREADY DONE — no task.** `archreview/plans/STATUS.md` in the working tree (modified,
|
||
uncommitted, per `git status`) now reads "Branch topology (HISTORICAL — as of 2026-07-08; since
|
||
merged + pushed)" (line 13), carries the "2026-07-12 UPDATE — … merged to master and pushed"
|
||
banner (lines 6-9), and strikes through "Nothing is pushed" (line 116). The stale claims are
|
||
corrected; committing the working-tree archreview updates ships it. **Mark done; do not re-plan.**
|
||
|
||
---
|
||
|
||
## Priority ordering
|
||
|
||
All items are Small; ordering is by risk-class, destructive-first-with-proof:
|
||
|
||
1. **U-5** — tracked proprietary binaries (license risk on every clone) — the one item with legal weight.
|
||
2. **U-4** — tracked planning files violating their own rule (trust/doc drift).
|
||
3. **S-3 + P-5** — CI truth fixes (one workflow file deleted, one job deleted).
|
||
4. **U-3** — operator-facing doc gap.
|
||
5. **C-5 + editorconfig** — trivial polish.
|
||
6. **C-4** — conditional on upstream; smallest honest fix if blocked.
|
||
7. **U-6** — untracked disk-only cleanup (no commit at all).
|
||
|
||
---
|
||
|
||
## 1. 07/U-5 — 7 orphaned proprietary AVEVA DLLs tracked in `lib/`
|
||
|
||
**Restatement:** 7 committed AVEVA/Wonderware vendor binaries sit in `lib/`, referenced by no
|
||
project — a redistribution/licence risk shipped in every clone. Leftovers from the retired
|
||
in-process MXAccess / Wonderware-sidecar era; the COM/bitness story now lives entirely in the
|
||
sibling `mxaccessgw` repo.
|
||
|
||
**Verification:** Confirmed (see summary). Zero-reference proof already executed read-only:
|
||
per-filename grep over all msbuild files + slnx = 0 hits; zero `HintPath` elements repo-wide.
|
||
|
||
**Proposed action — DELETE (`git rm -r lib/`) + gitignore guard.** There is no keep case: nothing
|
||
compiles against them, the gateway repo owns the MXAccess surface, and the binaries remain
|
||
retrievable from git history or the AVEVA SDK if ever needed. Add `lib/` to `.gitignore` to block
|
||
re-introduction. **Explicitly out of scope:** purging them from *history* (BFG/filter-repo) is a
|
||
separate, heavier decision that rewrites every clone; the immediate licence-hygiene win is removing
|
||
them from the tip. Note this in the commit message.
|
||
|
||
> **⚠️ MAINTAINER SIGN-OFF REQUIRED before the `git rm` runs.** The executor must re-run the proof
|
||
> (step 1) and show its empty output; delete only after the proof passes and the maintainer approves.
|
||
|
||
**Steps (exact commands):**
|
||
|
||
1. **Proof (must pass first — expect zero output from both):**
|
||
```bash
|
||
for dll in aahClient.dll aahClientCommon.dll aahClientManaged.dll \
|
||
ArchestrA.CloudHistorian.Contract.dll ArchestrA.MXAccess.dll \
|
||
Historian.CBE.dll Historian.DPAPI.dll; do
|
||
grep -rln "$dll" --include='*.csproj' --include='*.props' --include='*.targets' --include='*.slnx' .
|
||
done
|
||
grep -rn "HintPath" --include='*.csproj' --include='*.props' --include='*.targets' .
|
||
```
|
||
Expected: no output (verified 2026-07-12: no output).
|
||
2. `git rm -r lib/` — expect `rm 'lib/…'` × 7.
|
||
3. Append to `.gitignore` under a new `# Vendor binaries — never commit (license risk; see R2-12/U-5)` header: `lib/`
|
||
4. `dotnet build ZB.MOM.WW.OtOpcUa.slnx` — must succeed (proves nothing linked them).
|
||
5. Commit (see Task breakdown, commit 1).
|
||
6. Verify: `git ls-files lib/` → empty; `git status` clean.
|
||
|
||
**Effort:** Trivial. **Risk:** Low — zero-reference proven; blast radius nil at the tip. The only
|
||
residual is history retention (documented as out of scope).
|
||
|
||
---
|
||
|
||
## 2. 07/U-6 — retired `Historian.Wonderware*` husk directories on disk
|
||
|
||
**Restatement:** 5 retired `Driver.Historian.Wonderware*` directories linger under `src/Drivers/` +
|
||
`tests/Drivers/` after the HistorianGateway cutover retired the Wonderware backend.
|
||
|
||
**Verification:** Confirmed — but they are **untracked, gitignored `bin/`+`obj/` husks only**
|
||
(0 tracked files across all 5; absent from the slnx; zero csproj references). This is a disk-only
|
||
cleanup: **no git command, no commit** results from it.
|
||
|
||
**Proposed action — plain `rm -rf` of the 5 directories.** Keep the two classes of *tracked*
|
||
Wonderware residue: `docs/drivers/Historian.Wonderware.md` (deliberate retired stub, referenced by
|
||
CLAUDE.md) and the 3 `code-reviews/Driver.Historian.Wonderware*/findings.md` (closed-review
|
||
history). The scope brief's "verify absent from slnx + zero project references, then delete" is
|
||
satisfied: the projects were properly `git rm`'d in the retirement PR; only ignored build output
|
||
survived on this machine.
|
||
|
||
> **⚠️ Sign-off note:** destructive to local disk only (build artifacts). Proof step must still show
|
||
> "0 tracked files" before `rm -rf` runs.
|
||
|
||
**Steps (exact commands):**
|
||
|
||
1. **Proof (expect `0` and no slnx match):**
|
||
```bash
|
||
git ls-files src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware* \
|
||
tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware* | wc -l # → 0
|
||
grep -i wonderware ZB.MOM.WW.OtOpcUa.slnx # → exit 1
|
||
```
|
||
2. ```bash
|
||
rm -rf src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware \
|
||
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client \
|
||
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Contracts \
|
||
tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests \
|
||
tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client.Tests
|
||
```
|
||
3. Verify: `find src tests -maxdepth 3 -type d -iname '*Wonderware*'` → empty;
|
||
`git status` **unchanged** by the deletion (they were untracked).
|
||
|
||
**Effort:** Trivial. **Risk:** None (ignored build output; regenerable would-be, except the
|
||
projects no longer exist to regenerate it — which is the point).
|
||
|
||
---
|
||
|
||
## 3. 07/U-4 — tracked root planning files contradict their own "never stage" rule
|
||
|
||
**Restatement:** 5 point-in-time planning/working-notes files are committed at root; `pending.md:3`
|
||
itself declares the hard rule "never stage `pending.md` / `current.md`". All are stale snapshots
|
||
that now contradict `docs/` + CLAUDE.md (e.g. `pending.md` is "as of 2026-06-16", ~25 commits and
|
||
one whole backend cutover ago).
|
||
|
||
**Verification:** Confirmed — all 5 tracked; no `.gitignore` entries; the one inbound tracked
|
||
reference is `docs/Historian.md:397,399` → `stillpending.md`.
|
||
|
||
**Proposed action — per-file (contents were read; recommendations follow from content):**
|
||
|
||
| File | Content finding | Recommendation | Rationale |
|
||
|---|---|---|---|
|
||
| `pending.md` | Active session-notes ledger (71 KB); carries the "never stage" hard rule itself | **Untrack, keep locally:** `git rm --cached pending.md` + gitignore | Makes the file's own rule true without destroying live working notes |
|
||
| `current.md` | Galaxy standard-driver status "Updated 2026-06-13" — that effort fully shipped (Phases A/B/C on master; recorded in CLAUDE.md) | **Untrack, keep locally:** `git rm --cached current.md` + gitignore | Named by the same hard rule; content stale but it's a working-notes file by declared convention |
|
||
| `looseends.md` | 2026-05-18 snapshot; sole open item "#20 dev-rig rollout smoke" targets the **retired Wonderware sidecar rig** — moot since the gateway cutover | **Delete outright:** `git rm looseends.md` | Fully superseded; its one open item is against infrastructure that no longer exists |
|
||
| `stillpending.md` | The reconciled 2026-06-18 backlog audit (~95% shipped); **referenced by tracked `docs/Historian.md:397-399`** and by the memory index | **Archive-move:** `git mv stillpending.md docs/plans/2026-06-18-stillpending-backlog-audit.md` + update the `docs/Historian.md` reference | Referenced history — preserve under the established dated-plan convention rather than delete |
|
||
| `HISTORIAN-GATEWAY-INTEGRATION-ISSUES.md` | 2026-06-27 live-smoke findings that drove the provisioning-dormancy fix (file/line refs pinned to `245316d8`) | **Archive-move:** `git mv HISTORIAN-GATEWAY-INTEGRATION-ISSUES.md docs/plans/2026-06-27-historian-gateway-integration-issues.md` | Referenced investigative history (memory index cites it); dated-plan archive fits |
|
||
|
||
> **⚠️ MAINTAINER SIGN-OFF REQUIRED for the `looseends.md` deletion** (only truly destructive step —
|
||
> `git rm --cached` keeps local copies, `git mv` keeps content). The executor must show the
|
||
> maintainer this table before running step 2.
|
||
|
||
**Steps (exact commands):**
|
||
|
||
1. ```bash
|
||
git rm --cached pending.md current.md # untrack, keep on disk
|
||
```
|
||
2. ```bash
|
||
git rm looseends.md # delete (sign-off required)
|
||
git mv stillpending.md docs/plans/2026-06-18-stillpending-backlog-audit.md
|
||
git mv HISTORIAN-GATEWAY-INTEGRATION-ISSUES.md docs/plans/2026-06-27-historian-gateway-integration-issues.md
|
||
```
|
||
3. Append to `.gitignore` under `# Session working notes — never stage (pending.md's own hard rule)`:
|
||
```
|
||
/pending.md
|
||
/current.md
|
||
```
|
||
(root-anchored so a future `docs/plans/*pending*` archive is not swallowed).
|
||
4. Edit `docs/Historian.md:397-399`: repoint the two `stillpending.md` mentions at
|
||
`docs/plans/2026-06-18-stillpending-backlog-audit.md`.
|
||
5. Verify:
|
||
```bash
|
||
git ls-files | grep -E '^(pending|current|looseends|stillpending|HISTORIAN)' # → empty
|
||
git status --short | grep -E 'pending|current' # → nothing staged; files still on disk
|
||
git grep -n 'stillpending\.md' -- docs/ CLAUDE.md # → only the archived path
|
||
```
|
||
|
||
**Effort:** Small. **Risk:** Low — pure VCS hygiene; `--cached` + `mv` preserve all content; the one
|
||
deletion is a 2.8 KB fully-superseded snapshot recoverable from history.
|
||
|
||
---
|
||
|
||
## 4. 07/U-3 — `docs/Client.CLI.md` documents 8 of 13 commands
|
||
|
||
**Restatement:** the five operator alarm commands (`ack`, `confirm`, `shelve`, `enable`, `disable`)
|
||
ship in the CLI but have zero sections in `docs/Client.CLI.md`, while CLAUDE.md points operators at
|
||
that doc. (Correction to the report: the actual command set is **13**, not 14 — verified by
|
||
enumerating `[Command("…")]` attributes; 8 documented + 5 missing = 13.)
|
||
|
||
**Verification:** Confirmed. Enumerated command set vs `###` headings (see summary). Options
|
||
extracted from the command sources:
|
||
|
||
| Command | Options (from source) |
|
||
|---|---|
|
||
| `ack` (`AcknowledgeCommand.cs`) | `--node/-n` condition NodeId (required) · `--event-id/-e` hex EventId (required) · `--comment/-c` |
|
||
| `confirm` (`ConfirmCommand.cs`) | `--node/-n` (required) · `--event-id/-e` (required) · `--comment/-c` — two-stage ack |
|
||
| `shelve` (`ShelveCommand.cs`) | `--node/-n` (required) · `--kind/-k` `OneShot \| Timed \| Unshelve` (required) · `--duration/-d` seconds (required for `Timed`; converted to ms for the OPC UA call) |
|
||
| `enable` (`EnableCommand.cs`) | `--node/-n` (required) |
|
||
| `disable` (`DisableCommand.cs`) | `--node/-n` (required) |
|
||
|
||
All five inherit `CommandBase` connection options (`-u`, auth, security, failover) like the
|
||
documented commands.
|
||
|
||
**Proposed action — exactly these docs sections to add** to `docs/Client.CLI.md`, inserted after the
|
||
existing `### alarms` section (they are its operator-action counterparts) and before `### redundancy`:
|
||
|
||
1. A grouping intro `### Alarm operator commands (ack / confirm / shelve / enable / disable)` **or**
|
||
five flat `###` sections mirroring the existing per-command shape — prefer **five flat `###`
|
||
sections** (`### ack`, `### confirm`, `### shelve`, `### enable`, `### disable`) to match the
|
||
established one-heading-per-command structure, each with: synopsis line, options table, one
|
||
example invocation, expected output. Note in each that the server gates these on the **AlarmAck**
|
||
LDAP role (per CLAUDE.md §Scripted Alarm Ack/Shelve) and that `--event-id` comes from a prior
|
||
`alarms` subscription notification.
|
||
2. Cross-check terminology against `docs/ScriptedAlarms.md` §"Inbound operator ack/shelve".
|
||
3. Scan the doc's Overview/intro for any stale command-count claim ("8 commands"/"14 commands") and
|
||
correct to 13.
|
||
|
||
**Steps:** edit `docs/Client.CLI.md`; verify each command's documented options against
|
||
`dotnet run --project src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI -- <cmd> --help` output (or the
|
||
source attributes above, if not running the CLI).
|
||
|
||
**Effort:** Small. **Risk:** None (docs only).
|
||
|
||
---
|
||
|
||
## 5. 07/S-3 — `v2-e2e.yml` gates zero tests (permanent green no-op)
|
||
|
||
**Restatement:** the nightly workflow boots the docker-dev fleet then runs
|
||
`dotnet test --filter "Category=E2E"` — a filter that matches zero tests in the tree — and reports
|
||
green daily, training people to ignore the badge.
|
||
|
||
**Verification:** Confirmed — zero `Category=E2E` traits repo-wide (`grep -rn 'Category.*E2E'
|
||
tests/ --include='*.cs'` → exit 1); the workflow header self-documents the no-op.
|
||
|
||
**Proposed action — DELETE the workflow (recommended), not seed a test.** Rationale:
|
||
|
||
- **The fleet cannot boot on a hosted runner anyway.** `docker-dev/docker-compose.yml` binds the
|
||
shared GLAuth at `10.100.0.35:3893` (LAN-only, per CLAUDE.md) and builds locally-defined
|
||
`otopcua-*` images — so even a seeded E2E test would fail-or-flake at the "Build docker-dev
|
||
fleet" / "Wait for cluster" steps on GitHub/Gitea hosted runners. Seeding one real E2E test does
|
||
**not** make this workflow honest; it makes it red-for-infrastructure-reasons, which is worse
|
||
than absent.
|
||
- **The false-green cost is live today**; deletion removes it immediately at zero effort.
|
||
- **Nothing of value is lost.** The whole-solution CI leg (07/S-1, shipped) genuinely gates all 47
|
||
test projects; the `scripts/e2e/test-*.ps1` harnesses (the real E2E assets) are untouched. When
|
||
a real `E2ETests` project lands (the round-1 plan's option (b), still the right follow-on), it
|
||
should arrive **with its own workflow** including a zero-match guard (fail if `Category=E2E`
|
||
matches nothing) and a runner that can actually reach a fleet — a fresh design, not this husk.
|
||
- Alternative (round-1 option (a): keep the file, comment out `schedule:`, add a zero-match guard)
|
||
is acceptable if the maintainer wants the file as a placeholder — but a placeholder that cannot
|
||
run on its own runner documents an intention better served by one line in a tracked follow-up.
|
||
|
||
> **⚠️ Maintainer sign-off:** deleting a workflow file — confirm the nightly's absence won't be
|
||
> mistaken for a CI regression (it will simply stop appearing in Actions).
|
||
|
||
**Steps:**
|
||
```bash
|
||
git rm .github/workflows/v2-e2e.yml
|
||
```
|
||
Record the follow-up ("land tests/Server/…E2ETests + a new workflow with a zero-match guard when a
|
||
runner-reachable fleet exists") in the commit message and in `archreview/plans/STATUS.md`'s open
|
||
list. Also remove the now-dangling pointer in `v2-ci.yml`'s header comment (line 7-8: "Skips E2E …
|
||
that runs nightly via v2-e2e.yml") — fold into the P-5 edit below.
|
||
|
||
**Effort:** Trivial. **Risk:** Low — removes a job that has never executed a test.
|
||
|
||
---
|
||
|
||
## 6. 07/P-5 (new) — CI integration matrix fully redundant with the unit leg
|
||
|
||
**Restatement:** the `integration` matrix job (`v2-ci.yml:77-106`) re-restores, re-builds, and
|
||
re-runs `Host.IntegrationTests` + `OpcUaServer.IntegrationTests` under the identical filter the
|
||
whole-solution `unit-tests` leg already applies to the same two projects, fixtureless both times.
|
||
Its only unique output is a per-project skip tally the unit leg's trx already contains. (Note the
|
||
projects are not pure-skip — the in-process 2-node Akka harness tests, e.g. `HardKillFailoverTests`,
|
||
genuinely execute — but they execute in **both** jobs.)
|
||
|
||
**Verification:** Confirmed at `v2-ci.yml:58-62` (unit leg, whole slnx, same filter) vs `:77-106`
|
||
(matrix, same filter, `MIN_EXECUTED=0` report-only gate).
|
||
|
||
**Proposed action — DELETE the `integration` job (recommended).** The report offers two options:
|
||
delete, or make it earn its keep by adding the `mcr.microsoft.com/iotedge/opc-plc` `services:`
|
||
block + strict gate (S-2 option #2). Recommend **delete** for this batch because:
|
||
|
||
- The matrixed projects don't consume `OPCUA_SIM_ENDPOINT` — the opc-plc fixture serves the
|
||
OpcUaClient *driver* integration suite, which runs in the unit leg, not in this matrix. Making
|
||
this specific matrix "earn its keep" therefore means redesigning *which* projects it runs — a
|
||
real S-2-completion work item, not a dedupe.
|
||
- Deletion halves the remaining CI restore/build waste for free (per 07/P-1) and removes a
|
||
duplicate execution of the slowest harness tests on every push.
|
||
- The S-2 option-#2 follow-up (a fixture-backed leg with a strict gate, covering something the unit
|
||
leg cannot) stays tracked in the workflow's own comment lineage — preserve it in the commit
|
||
message and STATUS.md rather than in dead YAML.
|
||
|
||
**Steps:**
|
||
1. Edit `.github/workflows/v2-ci.yml`: delete the entire `integration:` job (lines 77-106) and the
|
||
header-comment line describing it (line 5), and update the header's E2E pointer (line 7-8) per
|
||
S-3 above. Keep `build` and `unit-tests` untouched.
|
||
2. Verify YAML: `python3 -c "import yaml,sys; yaml.safe_load(open('.github/workflows/v2-ci.yml'))"`
|
||
(or `yq`), then confirm on the first post-push run that `build` → `unit-tests` still gate.
|
||
|
||
**Effort:** Trivial. **Risk:** Low — no coverage is lost (both projects still run in the unit leg
|
||
under the strict `MIN_EXECUTED=1` gate); only duplicate execution is removed.
|
||
|
||
---
|
||
|
||
## 7. 07/C-5 — `StyleGuide.md` says "ScadaBridge"; no repo `.editorconfig`
|
||
|
||
*(The R2-12 brief labels this C-6; in the source report the StyleGuide + editorconfig finding is
|
||
**C-5** — C-6 is csproj-boilerplate dedup, out of scope here.)*
|
||
|
||
**Restatement:** the documentation style guide still carries the sister repo's product name; there
|
||
is no mechanical code-style anchor at all.
|
||
|
||
**Verification:** Confirmed — the file is at repo root **`StyleGuide.md`** (the report's
|
||
`docs/StyleGuide.md` path is wrong; noted for the next report pass), line 3: "This guide defines
|
||
writing conventions and formatting rules for all ScadaBridge documentation." No `.editorconfig`
|
||
tracked anywhere.
|
||
|
||
**Proposed action — the minimal fix, per the brief:**
|
||
1. `StyleGuide.md`: replace "ScadaBridge" → "OtOpcUa" on line 3, then
|
||
`grep -n "ScadaBridge" StyleGuide.md` and fix any further hits (verified: line 3 is in the head;
|
||
sweep the whole file at execution time).
|
||
2. Add a **minimal root `.editorconfig`** codifying the already-consistent conventions at
|
||
non-error severity (documentation-of-convention, not a reformat mandate — no `dotnet format`
|
||
CI step in this batch):
|
||
```ini
|
||
root = true
|
||
|
||
[*]
|
||
charset = utf-8
|
||
insert_final_newline = true
|
||
indent_style = space
|
||
|
||
[*.{cs,csproj,props,targets}]
|
||
indent_size = 4
|
||
|
||
[*.cs]
|
||
csharp_style_namespace_declarations = file_scoped:suggestion
|
||
dotnet_naming_rule.private_fields_underscore.severity = suggestion
|
||
dotnet_naming_rule.private_fields_underscore.symbols = private_fields
|
||
dotnet_naming_rule.private_fields_underscore.style = underscore_camel
|
||
dotnet_naming_symbols.private_fields.applicable_kinds = field
|
||
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
|
||
dotnet_naming_style.underscore_camel.capitalization = camel_case
|
||
dotnet_naming_style.underscore_camel.required_prefix = _
|
||
|
||
[*.{yml,yaml,json}]
|
||
indent_size = 2
|
||
```
|
||
3. Verify: `dotnet build ZB.MOM.WW.OtOpcUa.slnx` — the build must be **warning-delta-free** (all
|
||
severities `suggestion`; if any new warning appears in a `TreatWarningsAsErrors` project, drop
|
||
that rule to `suggestion` — nothing in this batch may fail a TWE build).
|
||
|
||
**Effort:** Trivial. **Risk:** Low — the one hazard is `.editorconfig` rules surfacing as warnings
|
||
in TWE'd projects; mitigated by suggestion-only severities + the build check.
|
||
|
||
---
|
||
|
||
## 8. 07/C-4 — 3 xunit 2.9.2 holdout projects
|
||
|
||
**Restatement:** `AdminUI.Tests`, `ControlPlane.Tests`, `Runtime.Tests` still reference `xunit`
|
||
2.9.2 while 44 projects are on `xunit.v3` — a mixed v2/v3 population as a moving part in the new
|
||
whole-solution CI leg.
|
||
|
||
**Verification:** Confirmed — but with a decisive wrinkle the report undersells: **all three csprojs
|
||
carry an explicit comment that `Akka.TestKit.Xunit2` pins them to xunit v2** ("TestKit isn't ported
|
||
to v3 yet"). Akka is pinned at 1.5.62. The holdouts are not drift; they are a documented upstream
|
||
block, discoverable only at the csproj level today.
|
||
|
||
**Proposed action — two-branch, decided by a feed probe (exact commands below):**
|
||
|
||
- **Branch A (unblocked):** if an xunit-v3-compatible Akka TestKit now exists at/near Akka 1.5.62
|
||
(candidate IDs: `Akka.TestKit.Xunit3`, `Akka.TestKit.Xunit.v3`), migrate all 3 projects:
|
||
swap `Akka.TestKit.Xunit2`→the v3 package + `xunit`→`xunit.v3` in each csproj, fix any
|
||
`Akka.TestKit.Xunit2` namespace usings, run each suite, then **delete the `xunit` 2.9.2 pin**
|
||
from `Directory.Packages.props:108`. Effort M if taken — spill to a follow-up PR if test churn
|
||
exceeds trivial.
|
||
- **Branch B (still blocked — expected):** do the honest CPM alignment instead: (1) bump the pin to
|
||
the latest 2.9.x patch (`2.9.3`) if the feed has it; (2) **add the blocker rationale as a comment
|
||
in `Directory.Packages.props`** next to line 108, house-style (mirroring the CVE-suppress
|
||
carve-out): `<!-- xunit v2 pin: AdminUI/ControlPlane/Runtime.Tests are held on v2 by
|
||
Akka.TestKit.Xunit2 (no v3 TestKit as of Akka 1.5.62). Remove when Akka ships an xunit.v3
|
||
TestKit and the 3 holdouts migrate. -->` — so the removal condition lives at the pin, not just in
|
||
3 scattered csprojs.
|
||
|
||
**Steps:**
|
||
1. Probe:
|
||
```bash
|
||
for id in Akka.TestKit.Xunit3 Akka.TestKit.Xunit.v3; do
|
||
curl -s "https://api.nuget.org/v3-flatcontainer/$(echo $id | tr 'A-Z' 'a-z')/index.json" | head -c 400; echo;
|
||
done
|
||
curl -s "https://api.nuget.org/v3-flatcontainer/xunit/index.json" | python3 -c "import json,sys; print([v for v in json.load(sys.stdin)['versions'] if v.startswith('2.9')])"
|
||
```
|
||
2. Take Branch A or B accordingly.
|
||
3. Verify (either branch): `dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests
|
||
tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests`
|
||
all green; whole-solution build clean.
|
||
|
||
**Effort:** Small (Branch B) / Medium (Branch A — cap it: if migration churn is non-trivial, ship
|
||
Branch B now and file the migration as a follow-up). **Risk:** Low (B) / Medium (A — TestKit swap
|
||
touches Akka actor tests; run all three suites).
|
||
|
||
---
|
||
|
||
## 9. 07/U-9 — STATUS.md stale "nothing pushed" claim — **ALREADY DONE**
|
||
|
||
Fixed in the working tree on 2026-07-12 (verified: topology header marked HISTORICAL, merge/push
|
||
banner added, resume-facts struck through and superseded). No task in this plan; it ships when the
|
||
working-tree archreview updates are committed. **Do not re-plan.**
|
||
|
||
---
|
||
|
||
## Task breakdown
|
||
|
||
Execution note: **T1/T3a/T5a/T9a are the zero-reference/probe proof gates — each destructive task is
|
||
blocked on its proof AND on maintainer sign-off.** Suggested commit grouping: 4 commits (removals /
|
||
planning files / CI workflows / docs+style) + 1 conditional (xunit).
|
||
|
||
### T1 — U-5 proof: zero references to the 7 `lib/` DLLs
|
||
**Classification:** trivial · **Estimated implement time:** 5 min · **Parallelizable with:** T3a, T5a, T6-pre, T9a · **Files:** none (read-only)
|
||
Run the two greps from item 1 step 1. Expected output: **empty** (verified empty 2026-07-12).
|
||
Then `dotnet build ZB.MOM.WW.OtOpcUa.slnx` → success. Present the empty output to the maintainer
|
||
for sign-off on T2.
|
||
|
||
### T2 — U-5: `git rm -r lib/` + gitignore guard *(blocked by T1 + sign-off)*
|
||
**Classification:** trivial · **Estimated implement time:** 5 min · **Parallelizable with:** none (commit 1) · **Files:** `lib/*` (delete ×7), `.gitignore`
|
||
`git rm -r lib/` (expect 7 `rm` lines) · append `lib/` to `.gitignore` · rebuild →
|
||
**Commit 1:** `chore(hygiene): remove 7 orphaned proprietary AVEVA DLLs from lib/ (07/U-5)` — note
|
||
zero-reference proof + history-purge-out-of-scope in the body. Verify `git ls-files lib/` → empty.
|
||
|
||
### T3a — U-6 proof: husk dirs are untracked and slnx-absent
|
||
**Classification:** trivial · **Estimated implement time:** 2 min · **Parallelizable with:** T1, T5a, T9a · **Files:** none (read-only)
|
||
Run item 2 step 1. Expected: `0` tracked files; slnx grep exit 1 (verified 2026-07-12).
|
||
|
||
### T3b — U-6: delete the 5 Wonderware husk directories *(blocked by T3a)*
|
||
**Classification:** trivial · **Estimated implement time:** 2 min · **Parallelizable with:** any (no git change) · **Files:** the 5 untracked dirs (disk only)
|
||
`rm -rf` per item 2 step 2. Verify `find src tests -maxdepth 3 -type d -iname '*Wonderware*'` →
|
||
empty and `git status` unchanged. **No commit.**
|
||
|
||
### T5a — U-4 proof + per-file confirmation
|
||
**Classification:** trivial · **Estimated implement time:** 5 min · **Parallelizable with:** T1, T3a, T9a · **Files:** none (read-only)
|
||
`git ls-files | grep -E '^(pending|current|looseends|stillpending|HISTORIAN)'` → the 5 files
|
||
(verified). Re-read `looseends.md` to confirm nothing new was added since 2026-05-18; show the
|
||
maintainer the item-3 recommendation table for sign-off (especially the `looseends.md` delete).
|
||
|
||
### T5b — U-4: untrack / archive / delete + gitignore + doc repoint *(blocked by T5a + sign-off)*
|
||
**Classification:** small · **Estimated implement time:** 15 min · **Parallelizable with:** T6/T7 (different files) · **Files:** the 5 root files, `.gitignore`, `docs/Historian.md`
|
||
Run item 3 steps 1-4 exactly. **Commit 2:** `chore(hygiene): untrack/archive root planning files;
|
||
honor pending.md's never-stage rule (07/U-4)`. Verify per item 3 step 5.
|
||
|
||
### T6 — S-3: delete `v2-e2e.yml` *(sign-off required)*
|
||
**Classification:** trivial · **Estimated implement time:** 5 min · **Parallelizable with:** T5b, T8, T9b · **Files:** `.github/workflows/v2-e2e.yml` (delete)
|
||
Pre-proof: `grep -rn 'Category.*E2E' tests/ --include='*.cs'` → exit 1 (verified). Then
|
||
`git rm .github/workflows/v2-e2e.yml`. Folded into **Commit 3** with T7. Record the
|
||
E2ETests-with-zero-match-guard follow-up in the commit body + STATUS.md open list.
|
||
|
||
### T7 — P-5: remove the redundant `integration` matrix job from `v2-ci.yml` *(pairs with T6)*
|
||
**Classification:** small · **Estimated implement time:** 10 min · **Parallelizable with:** T5b, T8, T9b · **Files:** `.github/workflows/v2-ci.yml`
|
||
Delete the `integration:` job (`v2-ci.yml:77-106`) + header lines referencing it and the deleted
|
||
nightly (lines 5, 7-8). YAML-validate. **Commit 3:** `ci(hygiene): delete no-op nightly E2E workflow
|
||
+ redundant integration matrix (07/S-3, 07/P-5)` — body notes the S-2 option-#2 fixture-leg
|
||
follow-up. Verify on first post-push run: `build` → `unit-tests` green, no `integration` job.
|
||
|
||
### T8 — U-3: add the 5 missing CLI command sections
|
||
**Classification:** small · **Estimated implement time:** 30 min · **Parallelizable with:** T6, T7, T9b, T10 · **Files:** `docs/Client.CLI.md`
|
||
Add `### ack`, `### confirm`, `### shelve`, `### enable`, `### disable` after `### alarms`, per the
|
||
item-4 options table (synopsis + options + example + AlarmAck-role note + event-id-from-alarms
|
||
note); fix any stale command-count claim to 13. Cross-check `docs/ScriptedAlarms.md` terminology.
|
||
Part of **Commit 4**.
|
||
|
||
### T9a — C-5 proof: ScadaBridge sweep + editorconfig absence
|
||
**Classification:** trivial · **Estimated implement time:** 2 min · **Parallelizable with:** T1, T3a, T5a · **Files:** none (read-only)
|
||
`grep -n "ScadaBridge" StyleGuide.md` (expect line 3; note any others) ·
|
||
`git ls-files '*.editorconfig'` → empty (verified).
|
||
|
||
### T9b — C-5: StyleGuide fix + minimal `.editorconfig` *(blocked by T9a)*
|
||
**Classification:** trivial · **Estimated implement time:** 15 min · **Parallelizable with:** T8, T10 · **Files:** `StyleGuide.md`, new `.editorconfig`
|
||
Apply item 7 steps 1-2; rebuild and confirm zero new warnings in TWE'd projects. With T8 as
|
||
**Commit 4:** `docs(hygiene): document 5 alarm operator CLI commands; fix StyleGuide product name;
|
||
add minimal .editorconfig (07/U-3, 07/C-5)`.
|
||
|
||
### T10 — C-4: xunit holdouts — probe, then migrate or document the pin
|
||
**Classification:** small (Branch B) / medium (Branch A) · **Estimated implement time:** 20 min (B) / 2 h (A) · **Parallelizable with:** T8, T9b · **Files:** `Directory.Packages.props`; Branch A also the 3 holdout csprojs + their test sources
|
||
Run the item-8 probe. Branch B (expected): bump pin to latest 2.9.x + add the blocker comment at
|
||
`Directory.Packages.props:108`; run the 3 suites. Branch A: migrate per item 8 (cap effort; spill
|
||
to a follow-up if churn is non-trivial). **Commit 5:** `test(hygiene): document/align the xunit v2
|
||
holdout pin (07/C-4)` (or the migration equivalent). Verify: 3 suites green + whole-solution build.
|
||
|
||
### T11 — Final verification pass *(blocked by all above)*
|
||
**Classification:** small · **Estimated implement time:** 15 min · **Parallelizable with:** none
|
||
```bash
|
||
dotnet build ZB.MOM.WW.OtOpcUa.slnx
|
||
dotnet test ZB.MOM.WW.OtOpcUa.slnx --filter "Category!=E2E&Category!=LiveIntegration"
|
||
git ls-files lib/ # empty
|
||
git ls-files | grep -E '^(pending|current|looseends|stillpending|HISTORIAN)' # empty
|
||
find src tests -maxdepth 3 -type d -iname '*Wonderware*' # empty
|
||
ls .github/workflows/ # v2-ci.yml only (of the two)
|
||
git log --oneline -6 # the 4-5 hygiene commits
|
||
```
|
||
Expected: build + tests green, all hygiene assertions empty/clean. Update
|
||
`archreview/plans/STATUS.md` marking R2-12 items done (U-9 was already marked).
|
||
|
||
**Overall effort: Small (~half a day; +up to 2 h only if C-4 Branch A opens).**
|
||
|
||
---
|
||
|
||
## Execution deviations (R2-12)
|
||
|
||
Recorded during execution on branch `r2/12-repo-hygiene` (off master `1676c8f4`).
|
||
|
||
- **T3b (U-6, Wonderware husk dirs) — nothing to delete in this worktree; end state already correct.**
|
||
The 5 `Driver.Historian.Wonderware*` husk directories the plan targets are untracked
|
||
`bin/`+`obj/` build artifacts that lived in the *main checkout*. A fresh git worktree does not
|
||
carry untracked files, so `find . -type d -iname '*Wonderware*'` here returns only the tracked
|
||
`code-reviews/Driver.Historian.Wonderware{,.Client,.Client.Contracts}/findings.md` history (KEEP,
|
||
per plan). Proof: `git ls-files src/…/Wonderware* tests/…/Wonderware*` → 0; slnx grep → exit 1;
|
||
no husk dirs on disk. No `rm -rf` was needed and no commit results. (The husks, if present in the
|
||
main checkout, are harmless ignored build output and will be reaped by a normal `git clean`/rebuild
|
||
there; they were never in VCS.)
|