Files
lmxopcua/docs/v2/implementation/phase-0-rename-and-net10.md
Joseph Doherty 5b8d708c58 Phase 0 entry gate + branch-naming convention fix
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>
2026-04-17 13:46:26 -04:00

270 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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**: 35 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:
- [ ] `v2` branch is at commit `a59ad2e` or later (decisions #1125 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 `<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/*/*.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 `<AssemblyName>` / `<RootNamespace>` / `<ProjectReference>` 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