Record Phase 0 entry baseline: 820 passing, 2 pre-existing failures (Client.CLI.Tests.SubscribeCommandTests.Execute_PrintsSubscriptionMessage and Tests.MxAccess.MxAccessClientMonitorTests.Monitor_ProbeDataChange_PreventsStaleReconnect), 0 build errors, 167 build warnings. The two failures exist on v2 as of commit 1189dc8 and are unrelated to the rename. Phase 0 exit gate adapts the requirement to "failure count = baseline (2); pass count ≥ baseline (820)".
Branch-naming convention updated in implementation/overview.md and phase-0 doc: cannot use `v2/phase-N-slug` form because git treats `/` as path separator and `v2` already exists as a branch, blocking creation of any `v2/...` branch. Convention is now `phase-N-slug` (no v2/ prefix).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
Phase 0 — Rename to OtOpcUa + .NET 10 Cleanup
Status: DRAFT — implementation plan for Phase 0 of the v2 build (
plan.md§6).Branch:
phase-0-renameEstimated duration: 3–5 working days Predecessor: none (first phase) Successor: Phase 1 (phase-1-configuration-and-admin-scaffold.md)
Phase Objective
Mechanically rename the existing v1 codebase from LmxOpcUa to OtOpcUa and verify all existing v1 tests still pass under the new names. No new functionality, no .NET 10 retargeting of Host or Historian.Aveva (those move in Phase 2 with the Galaxy split — they need to stay on .NET 4.8 because of MXAccess and Wonderware Historian SDK dependencies). All other projects are already on .NET 10 and stay there.
The phase exists as a clean checkpoint: future PRs reference OtOpcUa consistently, the rename is not entangled with semantic changes, and the diff is mechanical enough to review safely.
Scope — What Changes
| Concern | Change |
|---|---|
| Project names | ZB.MOM.WW.LmxOpcUa.* → ZB.MOM.WW.OtOpcUa.* (all 11 projects) |
| Solution file | ZB.MOM.WW.LmxOpcUa.slnx → ZB.MOM.WW.OtOpcUa.slnx |
| Namespaces | ZB.MOM.WW.LmxOpcUa root → ZB.MOM.WW.OtOpcUa root (all source files) |
| Assembly names | <AssemblyName> and <RootNamespace> in every csproj |
| Folder names | src/ZB.MOM.WW.LmxOpcUa.* → src/ZB.MOM.WW.OtOpcUa.*; same in tests/ |
Default appsettings.json keys |
Lmx* → Ot* only where the section name is product-bound (e.g. LmxOpcUa.Server → OtOpcUa.Server); leave MxAccess.* keys alone (those refer to the AVEVA product, not ours) |
| Service registration name | TopShelf service name LmxOpcUa → OtOpcUa (until Phase 1 swaps TopShelf for Microsoft.Extensions.Hosting) |
| Documentation | All docs/*.md references; CLAUDE.md |
| Repo name | NOT in scope for Phase 0 — repo rename happens in a separate ops step after exit gate clears |
Scope — What Does NOT Change
| Item | Reason |
|---|---|
.NET Framework 4.8 target on Host and Historian.Aveva |
MXAccess COM is 32-bit only; Wonderware Historian SDK is .NET 4.8. Both move to Galaxy.Host (still .NET 4.8 x86) in Phase 2. |
.NET 10 target on Client.CLI / Client.Shared / Client.UI / all Tests |
Already there (verified 2026-04-17 via grep TargetFramework src/*/*.csproj). |
| Project structure (no new projects) | New projects (Configuration, Core, Core.Abstractions, Server, Admin) are added in Phase 1, not Phase 0. |
| Galaxy / MXAccess implementation | Stays in OtOpcUa.Host for now; Phase 2 splits it into Proxy/Host/Shared. |
master branch / production deployments |
Untouched — v2 work all happens on the v2 branch. |
OPC UA ApplicationUri defaults |
Currently include LmxOpcUa — leave as-is to avoid breaking existing client trust during v1/v2 coexistence. New ApplicationUri defaults land in Phase 1 alongside the cluster model. |
| MxAccess product references in docs / code | "MxAccess" is AVEVA's product name, not ours. Stays. |
Entry Gate Checklist
Verify all before opening the phase-0-rename branch:
v2branch is at commita59ad2eor later (decisions #1–125 captured)git statusis clean onv2dotnet test ZB.MOM.WW.LmxOpcUa.slnxpasses locally with zero failing tests, baseline test count recordeddotnet build ZB.MOM.WW.LmxOpcUa.slnxsucceeds with zero errors and ≤ baseline warning count- All design docs reviewed by the implementation lead:
docs/v2/plan.md,docs/v2/config-db-schema.md,docs/v2/admin-ui.md,docs/v2/driver-specs.md,docs/v2/driver-stability.md,docs/v2/implementation/overview.md - Decision #9 (rename to OtOpcUa as step 1) re-read and confirmed
- No other developers have open work on
v2that would conflict with bulk renames
Evidence file: docs/v2/implementation/entry-gate-phase-0.md recording date, baseline test count, signoff name.
Task Breakdown
Task 0.1 — Inventory references
Generate a complete map of every place LmxOpcUa appears:
grep -rln "LmxOpcUa" --include="*.cs" --include="*.csproj" --include="*.slnx" --include="*.json" --include="*.md" --include="*.razor" .
Save the result to docs/v2/implementation/phase-0-rename-inventory.md (gitignored after phase completes).
Acceptance:
- Inventory file exists, lists every reference grouped by file type
- Reviewer agrees inventory is complete (cross-check against
git grep -i lmxfor case-sensitivity bugs)
Task 0.2 — Rename project folders
Per project (11 projects total — 5 src + 6 tests):
git mv src/ZB.MOM.WW.LmxOpcUa.Client.CLI src/ZB.MOM.WW.OtOpcUa.Client.CLI
git mv src/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.LmxOpcUa.Client.CLI.csproj \
src/ZB.MOM.WW.OtOpcUa.Client.CLI/ZB.MOM.WW.OtOpcUa.Client.CLI.csproj
Repeat for: Client.Shared, Client.UI, Historian.Aveva, Host, and all 6 test projects.
Use git mv (not mv + git rm/git add) to preserve history.
Acceptance:
ls src/shows onlyZB.MOM.WW.OtOpcUa.*foldersls tests/shows onlyZB.MOM.WW.OtOpcUa.*foldersgit log --followon a renamed file shows continuous history pre-rename
Task 0.3 — Rename solution file
git mv ZB.MOM.WW.LmxOpcUa.slnx ZB.MOM.WW.OtOpcUa.slnx
Edit the .slnx to update every project path reference inside it.
Acceptance:
ZB.MOM.WW.OtOpcUa.slnxexists and references the renamed project pathsdotnet sln list(ordotnet buildagainst the slnx) succeeds
Task 0.4 — Update csproj contents
For every csproj:
- Update
<AssemblyName>if explicitly set - Update
<RootNamespace>if explicitly set - Update
<ProjectReference Include=...>paths for inter-project refs - Update
<PackageId>if any project ships as a NuGet (none currently expected, but verify)
Acceptance:
grep -rl "LmxOpcUa" src/*/*.csproj tests/*/*.csprojreturns emptydotnet restoresucceeds with no missing project references
Task 0.5 — Bulk-rename namespaces in source files
Run the rename across all .cs and .razor files:
grep -rl "ZB.MOM.WW.LmxOpcUa" --include="*.cs" --include="*.razor" . \
| xargs sed -i 's/ZB\.MOM\.WW\.LmxOpcUa/ZB.MOM.WW.OtOpcUa/g'
Acceptance:
grep -rln "ZB.MOM.WW.LmxOpcUa" --include="*.cs" --include="*.razor" .returns emptydotnet build ZB.MOM.WW.OtOpcUa.slnxsucceeds
Task 0.6 — Update appsettings.json + service hosting
In src/ZB.MOM.WW.OtOpcUa.Host/appsettings.json and equivalents:
- Rename product-named sections:
LmxOpcUa.Server→OtOpcUa.Server(if present) - Leave
MxAccess,Galaxy,Historiankeys untouched (those are external product names) - Update TopShelf
ServiceNameconstant fromLmxOpcUa→OtOpcUa
Acceptance:
- Service install (
dotnet run --project src/.../Host install) registers asOtOpcUa - Service uninstall + reinstall cycle succeeds on a Windows test box
Task 0.7 — Update documentation references
CLAUDE.md: replaceLmxOpcUareferences withOtOpcUain product-naming contexts; leaveMxAccess/MXAccessreferences alonedocs/*.md(existing v1 docs): same patterndocs/v2/*.md: already usesOtOpcUa— verify with grep
Acceptance:
grep -rln "LmxOpcUa" docs/ CLAUDE.mdreturns only references that explicitly need to retain the old name (e.g. historical sections, change log)- Each retained reference has a comment explaining why
Task 0.8 — Run full test suite + smoke test
dotnet build ZB.MOM.WW.OtOpcUa.slnx
dotnet test ZB.MOM.WW.OtOpcUa.slnx
Plus manual smoke test of Client.CLI against a running v1 OPC UA server:
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- connect -u opc.tcp://localhost:4840
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Client.CLI -- browse -u opc.tcp://localhost:4840 -r -d 2
Acceptance:
- Test count matches the baseline recorded at entry gate; zero failing tests
- Smoke test produces equivalent output to baseline (capture both, diff)
Task 0.9 — Update build commands in CLAUDE.md
The Build Commands section currently references ZB.MOM.WW.LmxOpcUa.slnx. Update to ZB.MOM.WW.OtOpcUa.slnx. Also update test paths.
Acceptance:
cat CLAUDE.md | grep -i lmxopcuareturns only retained-by-design references- A new developer cloning the repo can follow CLAUDE.md to build + test successfully
Compliance Checks (run at exit gate)
A phase-0-compliance.ps1 (or .sh) script runs all these and exits non-zero on any failure:
-
No stale
LmxOpcUareferences:grep -rln "LmxOpcUa" --include="*.cs" --include="*.csproj" --include="*.slnx" \ --include="*.json" --include="*.razor" . | wc -lExpected: 0 (or only allowlisted retained references)
-
All projects build:
dotnet build ZB.MOM.WW.OtOpcUa.slnx --warnaserrorExpected: success, warning count ≤ baseline
-
All tests pass:
dotnet test ZB.MOM.WW.OtOpcUa.slnxExpected: total count = baseline, failures = 0
-
Solution structure matches plan:
ls src/shows exactly:ZB.MOM.WW.OtOpcUa.{Client.CLI, Client.Shared, Client.UI, Historian.Aveva, Host}(5 entries)ls tests/shows the 6 test projects similarly renamed- No new projects yet (those land in Phase 1)
-
.NET targets unchanged:
- Client projects (CLI/Shared/UI):
net10.0 - Host + Historian.Aveva:
net48(split + retarget happens Phase 2) - All test projects: same targets as their SUT projects
- Client projects (CLI/Shared/UI):
-
Decision compliance: this phase implements decision #9 ("Rename to OtOpcUa as step 1"). Verify by:
grep -rln "decision #9\|Decision #9" src/ tests/Expected: at least one citation in CLAUDE.md or a phase-rename README explaining the mechanical scope.
-
Service registration works:
- Install service →
sc query OtOpcUareturns the service - Uninstall service →
sc query OtOpcUareturns "service does not exist"
- Install service →
Behavioral Smoke Test (exit-gate gate)
The v1 IntegrationTests suite is the authoritative behavioral spec for Phase 0. The renamed code must pass it identically.
dotnet test tests/ZB.MOM.WW.OtOpcUa.IntegrationTests --logger "console;verbosity=detailed"
Expected: pass count = baseline. Fail count = 0. Skipped count = baseline.
Completion Checklist
The exit gate signs off only when every item below is checked:
- All 11 projects renamed (5 src + 6 tests)
- Solution file renamed
- All
<AssemblyName>/<RootNamespace>/<ProjectReference>updated - All namespaces in source files updated
appsettings.jsonproduct-named sections updated; external product names untouched- TopShelf service name updated; install/uninstall cycle verified on a Windows host
docs/*.mdandCLAUDE.mdreferences updated; retained references explained- Build succeeds with zero errors and warning count ≤ baseline
- Test suite passes with zero failures and count = baseline
- Smoke test against running OPC UA server matches baseline output
phase-0-compliance.ps1script runs and exits 0- Adversarial review of the phase diff (
/codex:adversarial-review --base v2) — findings closed or deferred with rationale - PR opened against
v2, includes: link to this doc, link to exit-gate record, compliance script output, adversarial review output - Reviewer signoff (one reviewer beyond the implementation lead)
exit-gate-phase-0.mdrecorded with all of the above
After the PR merges, repo rename (lmxopcua → otopcua on Gitea) happens as a separate ops step — out of scope for Phase 0.
Risks and Mitigations
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
Bulk sed rename breaks string literals (e.g. "LmxOpcUa" used as a runtime identifier) |
Medium | Medium | Inventory step (0.1) flags string literals separately; rename them deliberately, not via bulk sed |
| MxAccess / Galaxy / Wonderware references accidentally renamed | Low | High (breaks COM interop) | Inventory step (0.1) calls out external product names explicitly; bulk rename targets only ZB.MOM.WW.LmxOpcUa (with namespace prefix), not bare LmxOpcUa |
| Test count drops silently because a test project doesn't get re-discovered | Medium | High | Baseline test count captured at entry gate; exit gate compares exactly |
.slnx references break and projects disappear from solution view |
Low | Medium | dotnet sln list after Task 0.3 verifies all projects load |
| TopShelf service install fails on a hardened Windows box (UAC, signing) | Low | Low | Manual install/uninstall cycle is part of Task 0.6 acceptance |
| Long-lived branches diverge while phase 0 is in flight | Medium | Low | Phase 0 expected duration ≤ 5 days; coordinate that no other v2 work merges during the phase |
Out of Scope (do not do in Phase 0)
- Adding any new project (Configuration, Admin, Core, Server, Driver.* — all Phase 1+)
- Splitting Host into Galaxy.Proxy/Host/Shared (Phase 2)
- Migrating Host/Historian.Aveva to .NET 10 (Phase 2 — when Galaxy is split, the .NET 4.8 x86 piece becomes Galaxy.Host and the rest can move)
- Replacing TopShelf with
Microsoft.Extensions.Hosting(Phase 1, decision #30) - Implementing the cluster / namespace / equipment data model (Phase 1)
- Changing any OPC UA wire behavior
- Renaming the Gitea repo