# 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-rename` > **Estimated 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 | `` and `` 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: - [ ] `v2` branch is at commit `a59ad2e` or later (decisions #1–125 captured) - [ ] `git status` is clean on `v2` - [ ] `dotnet test ZB.MOM.WW.LmxOpcUa.slnx` passes locally with **zero failing tests**, baseline test count recorded - [ ] `dotnet build ZB.MOM.WW.LmxOpcUa.slnx` succeeds 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 `v2` that 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: ```bash 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 lmx` for case-sensitivity bugs) ### Task 0.2 — Rename project folders Per project (11 projects total — 5 src + 6 tests): ```bash 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 only `ZB.MOM.WW.OtOpcUa.*` folders - `ls tests/` shows only `ZB.MOM.WW.OtOpcUa.*` folders - `git log --follow` on a renamed file shows continuous history pre-rename ### Task 0.3 — Rename solution file ```bash 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.slnx` exists and references the renamed project paths - `dotnet sln list` (or `dotnet build` against the slnx) succeeds ### Task 0.4 — Update csproj contents For every csproj: - Update `` if explicitly set - Update `` if explicitly set - Update `` paths for inter-project refs - Update `` if any project ships as a NuGet (none currently expected, but verify) **Acceptance**: - `grep -rl "LmxOpcUa" src/*/*.csproj tests/*/*.csproj` returns empty - `dotnet restore` succeeds with no missing project references ### Task 0.5 — Bulk-rename namespaces in source files Run the rename across all `.cs` and `.razor` files: ```bash 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 empty - `dotnet build ZB.MOM.WW.OtOpcUa.slnx` succeeds ### 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`, `Historian` keys untouched (those are external product names) - Update TopShelf `ServiceName` constant from `LmxOpcUa` → `OtOpcUa` **Acceptance**: - Service install (`dotnet run --project src/.../Host install`) registers as `OtOpcUa` - Service uninstall + reinstall cycle succeeds on a Windows test box ### Task 0.7 — Update documentation references - `CLAUDE.md`: replace `LmxOpcUa` references with `OtOpcUa` in product-naming contexts; leave `MxAccess` / `MXAccess` references alone - `docs/*.md` (existing v1 docs): same pattern - `docs/v2/*.md`: already uses `OtOpcUa` — verify with grep **Acceptance**: - `grep -rln "LmxOpcUa" docs/ CLAUDE.md` returns 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 ```bash 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: ```bash 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 lmxopcua` returns 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: 1. **No stale `LmxOpcUa` references**: ``` grep -rln "LmxOpcUa" --include="*.cs" --include="*.csproj" --include="*.slnx" \ --include="*.json" --include="*.razor" . | wc -l ``` Expected: 0 (or only allowlisted retained references) 2. **All projects build**: ``` dotnet build ZB.MOM.WW.OtOpcUa.slnx --warnaserror ``` Expected: success, warning count ≤ baseline 3. **All tests pass**: ``` dotnet test ZB.MOM.WW.OtOpcUa.slnx ``` Expected: total count = baseline, failures = 0 4. **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) 5. **.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 6. **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. 7. **Service registration works**: - Install service → `sc query OtOpcUa` returns the service - Uninstall service → `sc query OtOpcUa` returns "service does not exist" ## 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. ```bash 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 `` / `` / `` updated - [ ] All namespaces in source files updated - [ ] `appsettings.json` product-named sections updated; external product names untouched - [ ] TopShelf service name updated; install/uninstall cycle verified on a Windows host - [ ] `docs/*.md` and `CLAUDE.md` references 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.ps1` script 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.md` recorded 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