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.
36 KiB
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: deletelib/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/, retiredDriver.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/*.slnxin 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}andtests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware{.Tests,.Client.Tests}. Each contains onlybin/+obj/(no source, no csproj).git ls-filesover 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); zeroHistorian.Wonderwarehits in any csproj. Tracked Wonderware residue is only the intentional retired stubdocs/drivers/Historian.Wonderware.md(keep — CLAUDE.md points at it) and 3code-reviews/Driver.Historian.Wonderware*/findings.md(keep — closed-review history). - U-4 CONFIRMED —
git ls-filesshows 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.mdline 3 still carries the hard rule "never stagepending.md/current.md"..gitignorehas no entry for any of them (nor forlib/). One tracked cross-reference exists:docs/Historian.md:397-399citesstillpending.mdby name. - U-3 CONFIRMED (count corrected) — the CLI has 13 commands, not the report's 14:
[Command("…")]attributes insrc/Client/ZB.MOM.WW.OtOpcUa.Client.CLI/Commands/are exactlyack, alarms, browse, confirm, connect, disable, enable, historyread, read, redundancy, shelve, subscribe, write(13 files, 13 attributes).docs/Client.CLI.mdhas###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.ymlheader (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 at10.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: theintegrationmatrix re-restores, re-builds, and re-runsHost.IntegrationTests+OpcUaServer.IntegrationTestsunder the identical filter (Category!=E2E&Category!=LiveIntegration) the whole-solutionunit-testsleg (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(notdocs/StyleGuide.mdas the report cites); line 3 reads "…for all ScadaBridge documentation". No.editorconfigexists anywhere (git ls-files '*.editorconfig'→ empty). - C-4 CONFIRMED (with a load-bearing wrinkle) —
Directory.Packages.props:108pinsxunit2.9.2; exactly 3 csprojs reference it (AdminUI.Tests,ControlPlane.Tests,Runtime.Tests; 44 projects are onxunit.v31.1.0). All three holdout csprojs carry an explicit comment:Akka.TestKit.Xunit2ties 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.mdin the working tree (modified, uncommitted, pergit 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:
- U-5 — tracked proprietary binaries (license risk on every clone) — the one item with legal weight.
- U-4 — tracked planning files violating their own rule (trust/doc drift).
- S-3 + P-5 — CI truth fixes (one workflow file deleted, one job deleted).
- U-3 — operator-facing doc gap.
- C-5 + editorconfig — trivial polish.
- C-4 — conditional on upstream; smallest honest fix if blocked.
- 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 rmruns. 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):
- Proof (must pass first — expect zero output from both):
Expected: no output (verified 2026-07-12: no output).
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' . git rm -r lib/— expectrm 'lib/…'× 7.- Append to
.gitignoreunder a new# Vendor binaries — never commit (license risk; see R2-12/U-5)header:lib/ dotnet build ZB.MOM.WW.OtOpcUa.slnx— must succeed (proves nothing linked them).- Commit (see Task breakdown, commit 1).
- Verify:
git ls-files lib/→ empty;git statusclean.
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 -rfruns.
Steps (exact commands):
- Proof (expect
0and no slnx match):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 -
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 - Verify:
find src tests -maxdepth 3 -type d -iname '*Wonderware*'→ empty;git statusunchanged 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.mddeletion (only truly destructive step —git rm --cachedkeeps local copies,git mvkeeps content). The executor must show the maintainer this table before running step 2.
Steps (exact commands):
-
git rm --cached pending.md current.md # untrack, keep on disk -
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 - Append to
.gitignoreunder# Session working notes — never stage (pending.md's own hard rule):(root-anchored so a future/pending.md /current.mddocs/plans/*pending*archive is not swallowed). - Edit
docs/Historian.md:397-399: repoint the twostillpending.mdmentions atdocs/plans/2026-06-18-stillpending-backlog-audit.md. - Verify:
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:
- 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-idcomes from a prioralarmssubscription notification. - Cross-check terminology against
docs/ScriptedAlarms.md§"Inbound operator ack/shelve". - 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.ymlbinds the shared GLAuth at10.100.0.35:3893(LAN-only, per CLAUDE.md) and builds locally-definedotopcua-*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-*.ps1harnesses (the real E2E assets) are untouched. When a realE2ETestsproject 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 ifCategory=E2Ematches 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:
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:
- Edit
.github/workflows/v2-ci.yml: delete the entireintegration: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. Keepbuildandunit-testsuntouched. - Verify YAML:
python3 -c "import yaml,sys; yaml.safe_load(open('.github/workflows/v2-ci.yml'))"(oryq), then confirm on the first post-push run thatbuild→unit-testsstill 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:
StyleGuide.md: replace "ScadaBridge" → "OtOpcUa" on line 3, thengrep -n "ScadaBridge" StyleGuide.mdand fix any further hits (verified: line 3 is in the head; sweep the whole file at execution time).- Add a minimal root
.editorconfigcodifying the already-consistent conventions at non-error severity (documentation-of-convention, not a reformat mandate — nodotnet formatCI step in this batch):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 - Verify:
dotnet build ZB.MOM.WW.OtOpcUa.slnx— the build must be warning-delta-free (all severitiessuggestion; if any new warning appears in aTreatWarningsAsErrorsproject, drop that rule tosuggestion— 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: swapAkka.TestKit.Xunit2→the v3 package +xunit→xunit.v3in each csproj, fix anyAkka.TestKit.Xunit2namespace usings, run each suite, then delete thexunit2.9.2 pin fromDirectory.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 inDirectory.Packages.propsnext 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:
- Probe:
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')])" - Take Branch A or B accordingly.
- 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.Testsall 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-testsgreen, nointegration` 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
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 untrackedbin/+obj/build artifacts that lived in the main checkout. A fresh git worktree does not carry untracked files, sofind . -type d -iname '*Wonderware*'here returns only the trackedcode-reviews/Driver.Historian.Wonderware{,.Client,.Client.Contracts}/findings.mdhistory (KEEP, per plan). Proof:git ls-files src/…/Wonderware* tests/…/Wonderware*→ 0; slnx grep → exit 1; no husk dirs on disk. Norm -rfwas needed and no commit results. (The husks, if present in the main checkout, are harmless ignored build output and will be reaped by a normalgit clean/rebuild there; they were never in VCS.)