18-task plan to make GalaxyMxGateway an Equipment-kind driver: retire the SystemPlatform NamespaceKind split + mirror + alias/relay machinery, author Galaxy points as ordinary equipment tags via the standard TagModal. Mostly deletion + a single EF migration dropping the per-kind unique constraint. Phases B (native alarms) + C (server historian) remain out of scope. Co-located .tasks.json for resume.
29 KiB
Galaxy Standard Driver — Phase A Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers-extended-cc:executing-plans to implement this plan task-by-task.
Goal: Make GalaxyMxGateway a valid Equipment-kind driver — retire the
SystemPlatform namespace split, the SystemPlatform mirror, and the
alias-tag/relay machinery — so Galaxy points are authored as ordinary equipment
Tags through the standard TagModal.
Architecture: Phase A is mostly deletion + relaxing validation, not new
runtime wiring. Galaxy live values already work on equipment tags (alias tags
prove it via direct driver subscription by FullName). We remove the
NamespaceKind.SystemPlatform enum value and every reference (validator,
composer/artifact mirror, authorization, registry, namespace-create UI), delete
the alias/relay AdminUI machinery, and add one EF migration to drop the
per-kind unique constraint. Native Galaxy alarms (Phase B) and server-side
historian (Phase C) are out of scope and get their own plans.
Tech Stack: .NET 10, EF Core (SQL Server, string-converted enum column),
Akka.NET, Blazor Server (no bUnit), xUnit + Shouldly. Design doc:
docs/plans/2026-06-12-galaxy-standard-driver-design.md (master 91cb9076).
Branch: feat/galaxy-standard-driver-phase-a off master 91cb9076.
Hard rules (every task)
- Stage by path; never
git add .. Never stagesql_login.txt,src/Server/ZB.MOM.WW.OtOpcUa.Host/pki/, orpending.md. - Never echo the gateway API key or the historian
SharedSecretinto a tracked file. - No force-push, no
--no-verify. - Commit per task, by explicit path.
Dependency spine (read before executing)
- Composition contract: Task 4 removes
GalaxyTagsfromPhase7CompositionResult+ theGalaxyTagPlanrecord. Tasks 5, 6, 7 depend on that shape change. Task 4 → 5 → 6, and 4 → 7. - Enum capstone: Task 9 removes the
NamespaceKind.SystemPlatform/NamespaceKindCompatibility.SystemPlatformenum values. It is the capstone — every reference must already be gone, so Task 9 isblockedByTasks 3, 4, 5, 8. Build must be green after Task 9. - Migration: Task 10 (EF migration + DbContext index change) is
blockedByTask 9 (the model must be settled). - AdminUI spine:
UnsTreeService.cs/IUnsTreeService.cs/EquipmentChildRows.cschange in lockstep (Task 12).EquipmentPage.razor(Task 13) andTagModal.razor(Task 14) depend on Task 12 and edit disjoint files (parallelizable with each other).
Task order
- Branch
- DriverTypeRegistry: GalaxyMxGateway → Equipment
- DraftValidator: drop Galaxy/SystemPlatform branches + rename alias validation
- Phase7Composer: remove Galaxy mirror producer + exception clause
- Phase7CompositionResult + GalaxyTagPlan: remove
GalaxyTags - DeploymentArtifact: remove Galaxy mirror decode + isGalaxyAlias exception
- Phase7Applier + OpcUaPublishActor: delete
MaterialiseGalaxyTags+ call site - Authorization: remove
ScopeKind.SystemPlatform+WalkSystemPlatform - Composer↔Artifact byte-parity test (Galaxy equipment tag)
- Capstone: remove
SystemPlatformenum values; build green - EF migration: drop per-kind unique constraint + delete orphaned rows
- Delete alias-only AdminUI files
- UnsTreeService/IUnsTreeService/EquipmentChildRows: strip alias/relay
- EquipmentPage.razor: strip alias UI
- TagModal.razor: wire Galaxy picker
- NamespaceEdit.razor: hide SystemPlatform option
- Docs: Uns.md + CLAUDE.md
- Verify: build + test + user-driven live
/run
Task 0: Create feature branch
Classification: trivial Estimated implement time: ~1 min Parallelizable with: none
Files: none (git only)
Step 1: From master at 91cb9076:
git checkout master
git rev-parse HEAD # expect 91cb9076...
git checkout -b feat/galaxy-standard-driver-phase-a
Step 2: Confirm clean tree (ignore untracked pending.md, sql_login.txt,
pki/, docs scratch files — never stage them).
Task 1: DriverTypeRegistry — GalaxyMxGateway allowed in Equipment
Classification: small Estimated implement time: ~3 min Parallelizable with: Task 11
Files:
- Modify:
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs(theDriverTypeMetadata(...)registration — itsAllowedNamespaceKindsarg) - Test:
tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/(registration metadata test, if one exists; otherwise assert in an existing factory test)
Context: DriverTypeMetadata.AllowedNamespaceKinds is a
NamespaceKindCompatibility flags value. Galaxy currently registers
NamespaceKindCompatibility.SystemPlatform. Change it to
NamespaceKindCompatibility.Equipment.
Step 1: Grep for the registration to find the exact line:
grep -n "NamespaceKindCompatibility\|DriverTypeMetadata(" src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs
Step 2: Change the AllowedNamespaceKinds argument from
NamespaceKindCompatibility.SystemPlatform to
NamespaceKindCompatibility.Equipment.
Step 3: Build the Galaxy driver project:
dotnet build src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy
Expected: 0 errors.
Step 4: Commit:
git add src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverFactoryExtensions.cs
git commit -m "feat(galaxy): register GalaxyMxGateway as an Equipment-kind driver"
Task 2: DraftValidator — drop Galaxy/SystemPlatform branches; rename alias validation
Classification: standard Estimated implement time: ~5 min Parallelizable with: Task 11
Files:
- Modify:
src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs:34,36,41-55,225-245 - Test:
tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/Validation/DraftValidatorTests.cs(or the existing validator test file — grep forDriverNamespaceKindMismatch/AliasTagMissingReference)
Context: ValidateDriverNamespaceCompatibility (225-245) currently rejects
GalaxyMxGateway under Equipment and rejects non-Galaxy under SystemPlatform.
After de-split, Galaxy under Equipment must be valid.
ValidateAliasTagFullName (41-55) requires TagConfig.FullName on a
Galaxy-bound equipment tag — keep this rule (a Galaxy equipment tag still
needs a reference) but rename it from "alias" to "Galaxy tag".
Step 1 (test first): Add/extend a test: a draft with a GalaxyMxGateway
driver in an Equipment namespace produces no DriverNamespaceKindMismatch
error; a Galaxy equipment tag with empty TagConfig still produces the
missing-reference error (now GalaxyTagMissingReference).
Run it — expect FAIL (current code rejects Galaxy under Equipment).
Step 2: In ValidateDriverNamespaceCompatibility, delete the switch's
Galaxy/SystemPlatform branches. With SystemPlatform gone the method has no
remaining rule — delete the method and its call at line 34. (If a reviewer
prefers a stub for future rules, leaving a => true no-op is acceptable, but
YAGNI says delete.)
Step 3: Rename ValidateAliasTagFullName → ValidateGalaxyTagFullName;
change the error code AliasTagMissingReference → GalaxyTagMissingReference
and the message wording from "Alias tag" to "Galaxy tag". Keep
ExtractTagConfigFullName (also used by {{equip}} validation elsewhere).
Step 4: Run the validator tests — expect PASS.
dotnet test tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests --filter "FullyQualifiedName~DraftValidator"
Step 5: Commit:
git add src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/Validation/DraftValidatorTests.cs
git commit -m "feat(validation): allow GalaxyMxGateway under Equipment; rename Galaxy-tag FullName check"
Task 3: Phase7Composer — remove the Galaxy mirror producer + exception clause
Classification: high-risk Estimated implement time: ~5 min Parallelizable with: none (composition contract; gates Tasks 4-8)
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs:338-360,365-374 - Test:
tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/(existing composer tests — grepGalaxyTag,MaterialiseGalaxy,equipmentTags)
Context: Lines 338-360 build galaxyTags (the SystemPlatform mirror,
t.EquipmentId is null + ns.Kind == SystemPlatform). Lines 365-374 build
equipmentTags with the Galaxy exception clause
(ns.Kind == NamespaceKind.Equipment || di.DriverType == "GalaxyMxGateway").
After de-split a Galaxy driver lives in an Equipment namespace, so the exception
is dead.
Step 1: Delete the galaxyTags producer (338-360) and stop returning it in
the Phase7CompositionResult it constructs (the GalaxyTags: argument —
coordinated with Task 4; if Task 4 hasn't run yet, temporarily pass []).
Step 2: In the equipmentTags predicate, remove
|| di.DriverType == "GalaxyMxGateway" so it reads
&& ns.Kind == NamespaceKind.Equipment. Update the now-stale comment.
Step 3: Update composer tests: a Galaxy equipment tag (EquipmentId set,
GalaxyMxGateway, Equipment-kind ns) appears in EquipmentTags; assert no
GalaxyTags are produced. Run:
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests --filter "FullyQualifiedName~Phase7Composer"
Step 4: Commit (composer + its tests).
Task 4: Phase7CompositionResult — remove GalaxyTags + GalaxyTagPlan
Classification: standard Estimated implement time: ~3 min Parallelizable with: none
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Composer.cs(thePhase7CompositionResultrecordGalaxyTagsmember + theGalaxyTagPlanrecord def, ~lines 21, 91-97)
Context: Once no producer emits Galaxy mirror plans, remove the contract.
Step 1: Remove the GalaxyTags property from Phase7CompositionResult and
delete the GalaxyTagPlan record. Fix the composer's result construction (drop
the GalaxyTags: arg).
Step 2: Build the OpcUaServer project; fix any remaining references in this project only (Task 5/6 handle the others). Expect references in DeploymentArtifact + Phase7Applier to break — those are Tasks 5 and 6.
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer
Step 3: Commit.
blockedBy: Task 3.
Task 5: DeploymentArtifact — remove Galaxy mirror decode + isGalaxyAlias exception
Classification: high-risk Estimated implement time: ~5 min Parallelizable with: none (byte-parity partner of composer)
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs:366-437,375-391,447-535,712-727
Context: BuildGalaxyTagPlans (366-437) mirrors the composer's mirror
producer; the systemPlatformNamespaces decode (375-391) reads Kind==1/"SystemPlatform";
BuildEquipmentTagPlans (447-535) carries the driverToType map +
isGalaxyAlias exception (513-514). All retire. Must stay byte-parity with the
composer (Task 3): equipment-tag plans must be identical for the same input.
Step 1: Delete BuildGalaxyTagPlans and its call site; remove the
GalaxyTags from the artifact's decoded composition (matching Task 4's contract).
Step 2: Delete the systemPlatformNamespaces decode block (375-391) and any
use of it.
Step 3: In BuildEquipmentTagPlans, remove the driverToType map and the
isGalaxyAlias exception; the equipment-tag gate becomes
if (!equipmentNamespaces.Contains(nsId)) continue;. Keep ExtractTagFullName
(712-727) — Galaxy equipment tags still carry FullName.
Step 4: Build Runtime + OpcUaServer:
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.Runtime
Expect 0 errors. Run existing DeploymentArtifact tests.
Step 5: Commit.
blockedBy: Task 4.
Task 6: Phase7Applier + OpcUaPublishActor — delete MaterialiseGalaxyTags + call site
Classification: standard Estimated implement time: ~3 min Parallelizable with: Task 7
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Applier.cs:152-179(deleteMaterialiseGalaxyTags) - Modify:
src/Server/ZB.MOM.WW.OtOpcUa.Runtime/OpcUa/OpcUaPublishActor.cs:~244-248(delete the_applier.MaterialiseGalaxyTags(composition);call + its comment)
Context: MaterialiseEquipmentTags (202-239) stays and already materializes
Galaxy equipment tags. Only the mirror pass retires.
Step 1: Delete MaterialiseGalaxyTags from Phase7Applier.
Step 2: Delete the call in OpcUaPublishActor (the GalaxyTags comment +
the MaterialiseGalaxyTags(composition) line).
Step 3: Build + run applier/publish tests (grep tests for MaterialiseGalaxy
and delete/adjust any).
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.Runtime
Step 4: Commit.
blockedBy: Task 4.
Task 7: Authorization — remove ScopeKind.SystemPlatform + WalkSystemPlatform
Classification: high-risk Estimated implement time: ~4 min Parallelizable with: Task 6
Files:
- Modify:
src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/NodeScope.cs:~57(removeSystemPlatformfrom theScopeKindenum) - Modify:
src/Core/ZB.MOM.WW.OtOpcUa.Core/Authorization/PermissionTrieBuilder.cs(theWalkSystemPlatformcall ~line 58 + method def ~82-90) - Test:
tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests/Authorization/(grepSystemPlatform)
Context: With no SystemPlatform namespaces possible, the SystemPlatform ACL walk is dead. Galaxy permissions now flow through the standard Equipment walk (Galaxy tags are equipment tags). This is authz — review carefully.
Step 1: Remove the WalkSystemPlatform call and method, and the
ScopeKind.SystemPlatform enum value. Confirm the Equipment walk covers Galaxy
equipment tags (it keys on EquipmentId/path, driver-agnostic).
Step 2: Update/remove any authz test asserting a SystemPlatform scope. Add an assertion (if a fixture exists) that a Galaxy equipment tag resolves under the Equipment permission trie.
Step 3: Build Core + run authz tests:
dotnet test tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests --filter "FullyQualifiedName~Authoriz"
Step 4: Commit.
blockedBy: Task 4 (compile order; avoids churn while composition contract shifts).
Task 8: Composer ↔ Artifact byte-parity test (Galaxy equipment tag)
Classification: standard Estimated implement time: ~5 min Parallelizable with: none
Files:
- Test:
tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/(extend or replace the existing alias-parity test — grepDeploymentArtifactAliasParity/Phase7ComposerAliasTag)
Context: The invariant: for the same draft input,
Phase7Composer.Compose(...).EquipmentTags and
DeploymentArtifact's decoded equipment-tag plans are identical when the
input includes a Galaxy equipment tag (EquipmentId set, GalaxyMxGateway,
Equipment-kind ns, TagConfig={"FullName":"DelmiaReceiver_001.DownloadPath"}).
This replaces the old alias-exception parity test with the now-canonical case.
Step 1 (test first): Author a fixture draft: one Equipment namespace, one
GalaxyMxGateway driver bound to it, one equipment tag with the Galaxy FullName.
Compose via the composer; serialize to artifact; decode; assert the equipment-tag
plan lists match exactly (FullName, NodeId scoping, ordering). Run — should pass
once Tasks 3+5 are in; if it fails, a parity divergence is a real defect.
Step 2: Commit.
blockedBy: Tasks 3, 5.
Task 9: CAPSTONE — remove SystemPlatform enum values; build green
Classification: high-risk Estimated implement time: ~4 min Parallelizable with: none
Files:
- Modify:
src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Enums/NamespaceKind.cs:13-17(removeSystemPlatform; keepEquipment,Simulated) - Modify:
src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/DriverTypeRegistry.cs:113-114(removeNamespaceKindCompatibility.SystemPlatform = 2)
Context: Capstone — every reference must already be gone (Tasks 1,2,3,5,7). Removing the value makes any missed reference a compile error, which is the point.
Step 1: Remove both enum members + their doc comments.
Step 2: Full-solution build:
dotnet build ZB.MOM.WW.OtOpcUa.slnx
Expected: 0 errors. Any error names a missed reference — fix it in the owning file (do not re-add the enum value).
Step 3: Full test pass on the touched suites:
dotnet test tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests
dotnet test tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests
Step 4: Commit.
blockedBy: Tasks 1, 2, 3, 5, 7. (Task 6 too — its call site references nothing SystemPlatform but keep it before the capstone for a clean build.)
Task 10: EF migration — drop per-kind unique constraint + delete orphaned rows
Classification: high-risk Estimated implement time: ~5 min Parallelizable with: none
Files:
- Modify:
src/Core/ZB.MOM.WW.OtOpcUa.Configuration/OtOpcUaConfigDbContext.cs:207-208(change the unique index from(ClusterId, Kind)to(ClusterId), renameUX_Namespace_Cluster_Kind→UX_Namespace_Cluster) - Create:
src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Migrations/<timestamp>_DropSystemPlatformNamespaceKind.cs(+ the.Designer.cs+ updatedOtOpcUaConfigDbContextModelSnapshot.cs, generated by the tool)
Context: Clean break. Keep the Kind column (still Equipment/Simulated,
string-converted) but stop enforcing one-per-kind; enforce one namespace per
cluster instead. Delete orphaned SystemPlatform rows (and dependents) so the
reduced enum never reads an unmapped value. Dev rigs reseed by hand.
Step 1: Edit OtOpcUaConfigDbContext.ConfigureNamespace: change
e.HasIndex(x => new { x.ClusterId, x.Kind }).IsUnique().HasDatabaseName("UX_Namespace_Cluster_Kind")
to e.HasIndex(x => x.ClusterId).IsUnique().HasDatabaseName("UX_Namespace_Cluster").
Step 2: Author the migration (the design-time factory reads
OTOPCUA_CONFIG_CONNECTION; migrations add does not connect, but set it to be
safe):
export OTOPCUA_CONFIG_CONNECTION="Server=10.100.0.35,14330;Database=OtOpcUaConfig;Trusted_Connection=True;TrustServerCertificate=True;"
dotnet ef migrations add DropSystemPlatformNamespaceKind --project src/Core/ZB.MOM.WW.OtOpcUa.Configuration
Step 3: Hand-edit the generated Up(...) so the order is safe:
// 1. delete dependents of SystemPlatform namespaces, then the namespaces themselves,
// so the reduced NamespaceKind enum never deserialises a 'SystemPlatform' string.
migrationBuilder.Sql(@"
DELETE FROM Tag WHERE DriverInstanceId IN (SELECT DriverInstanceId FROM DriverInstance WHERE NamespaceId IN (SELECT NamespaceId FROM Namespace WHERE Kind = 'SystemPlatform'));
DELETE FROM DriverInstance WHERE NamespaceId IN (SELECT NamespaceId FROM Namespace WHERE Kind = 'SystemPlatform');
DELETE FROM Namespace WHERE Kind = 'SystemPlatform';");
// 2. swap the unique index.
migrationBuilder.DropIndex(name: "UX_Namespace_Cluster_Kind", table: "Namespace");
migrationBuilder.CreateIndex(name: "UX_Namespace_Cluster", table: "Namespace", column: "ClusterId", unique: true);
(Verify the actual dependent tables/FKs against the snapshot before finalizing — adjust the DELETE cascade to match real FK names. A reviewer must confirm no dependent table is missed.)
Step 4: Provide a minimal Down(...) (recreate UX_Namespace_Cluster_Kind,
drop UX_Namespace_Cluster; data deletes are not reversible — note that in a
comment). Build the Configuration project.
Step 5: Commit (migration files + DbContext). Do not apply the migration here — it applies at deploy/runtime against the central SQL Server.
blockedBy: Task 9.
Task 11: Delete alias-only AdminUI files
Classification: small Estimated implement time: ~2 min Parallelizable with: Tasks 1, 2
Files (delete):
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/AliasTagModal.razor(+ any.razor.cs)src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/RelayConversion.cssrc/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/AliasTagEditDto.cssrc/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/AliasTagInput.cssrc/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Uns/RelayAliasConvert.razor(route/uns/convert-relays)
Context: These are alias/relay-only. Project won't compile until Task 12 removes the references — that's expected; this task is the file-level delete.
Step 1: git rm each file by path.
Step 2: Grep for any remaining RelayAliasConvert / convert-relays nav link
(e.g. a NavMenu/NavLink) and remove it.
Step 3: Commit (build will be red until Task 12 — note in the commit body).
Note for executor: because Tasks 11+12 leave AdminUI uncompilable between them, dispatch them as a bundle (one reviewer over the combined diff) rather than gating a build between them.
Task 12: UnsTreeService / IUnsTreeService / EquipmentChildRows — strip alias/relay
Classification: high-risk Estimated implement time: ~5 min Parallelizable with: none (AdminUI spine)
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.cs(delete:LoadAliasTagAsync207-215,LoadGalaxyGatewaysForEquipmentAsync778-798,CreateAliasTagAsync907-944,UpdateAliasTagAsync947-1001,ConvertRelayVirtualTagsToAliasesAsync1036-1214,CheckAliasDriverGuardAsync1595-1623,BuildAliasTagConfig~1628,BuildAliasTag~1637-1649, theEquipmentNamerecord + comparer ~1652-1670; inLoadTagsForEquipmentAsync86-118 remove the IsAlias/Source decoration 90-92 + 113-116). KeepExtractTagConfigFullName(used by{{equip}}validation). - Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs(delete members:LoadAliasTagAsync178-182,LoadGalaxyGatewaysForEquipmentAsync353-358,CreateAliasTagAsync388-399,UpdateAliasTagAsync401-413,ConvertRelayVirtualTagsToAliasesAsync558-567) - Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/EquipmentChildRows.cs:10-12(removeIsAlias/SourcefromEquipmentTagRow) - Delete:
tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/UnsTreeServiceAliasTagTests.cs,tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/UnsTreeServiceRelayConverterTests.cs
Context: These three files change in lockstep (interface + impl + row DTO).
LoadTagsForEquipmentAsync returns EquipmentTagRow(TagId, Name, DriverInstanceId, DataType, AccessLevel) after the IsAlias/Source removal.
Step 1: Delete the listed methods/members and the test files. Trim
LoadTagsForEquipmentAsync to the base tag load + new row shape.
Step 2: Build AdminUI together with Task 13 (this task alone leaves EquipmentPage referencing removed methods). Defer the green build to Task 13.
Step 3: Commit (service + interface + rows + deleted tests).
blockedBy: none for code, but bundle with Task 11 (see note).
Task 13: EquipmentPage.razor — strip alias UI; route Galaxy through standard TagModal
Classification: standard Estimated implement time: ~5 min Parallelizable with: Task 14
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Uns/EquipmentPage.razor(remove: "Add alias" button ~146, "Convert relay" button ~150, alias row display ~181-186, IsAlias edit/delete routing ~188-195, convert preview panel ~204-289,AliasTagModalinstance ~301-303, state vars ~426-440, handlersOpenAddAlias/OpenEditAlias/OnAliasSavedAsync/PreviewConvertRelaysAsync/ApplyConvertRelaysAsync~526-585, theLoadGalaxyGatewaysForEquipmentAsynccalls ~482/529, the Source column ~170/184)
Context: All tags (including Galaxy) use the existing "Add tag" → standard
TagModal path. The tag table shows no alias badge/Source column; Edit/Delete are
uniform for every row.
Step 1: Remove the alias UI, state, and handlers per the file list. Keep the Details / standard-Tag / VirtualTag / Alarm sections.
Step 2: Ensure the "Add tag" button opens the standard TagModal with the
equipment's driver list (which now includes Galaxy drivers — they're Equipment-kind
post-de-split).
Step 3: Build AdminUI (now compilable with Task 12):
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.AdminUI
Expected: 0 errors.
Step 4: Commit.
blockedBy: Task 12.
Task 14: TagModal.razor — wire the Galaxy address picker
Classification: standard Estimated implement time: ~5 min Parallelizable with: Task 13
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/TagModal.razor(picker dispatch: whenSelectedDriverType == "GalaxyMxGateway"showGalaxyAddressPickerBodyinside the existingDriverTagPicker, writing the chosen ref intoTagConfigas{"FullName":"..."}) - Possibly Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.csLoadTagDriversForEquipmentAsync— verify it returns the Galaxy driver'sDriverConfig(the picker opens a browse session with the gateway config); if the driver-list tuple lacksDriverConfig, extend it.
Context: The standard TagModal already dispatches address pickers by driver
type. GalaxyMxGateway keeps the raw-JSON TagConfig editor (no
TagConfigEditorMap entry needed) — the picker just fills FullName. The Galaxy
picker (GalaxyAddressPickerBody) needs the selected gateway's DriverConfig to
open a live browse session via IBrowserSessionService.OpenAsync("GalaxyMxGateway", configJson) (same call the old AliasTagModal made).
Step 1: Add the GalaxyMxGateway → GalaxyAddressPickerBody case to the
picker dispatch, mirroring how Modbus/S7 pickers are wired and how the old
AliasTagModal built the result (tag.attribute dot-join → {"FullName":...}).
Step 2: Confirm LoadTagDriversForEquipmentAsync surfaces Galaxy drivers +
their config to the modal; extend the returned shape if needed.
Step 3: Build AdminUI:
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.AdminUI
Step 4: Commit. (Razor behavior is proven live in Task 17 — no bUnit.)
blockedBy: Task 12.
Task 15: NamespaceEdit.razor — hide the SystemPlatform option
Classification: small Estimated implement time: ~2 min Parallelizable with: Tasks 13, 14
Files:
- Modify:
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Clusters/NamespaceEdit.razor(theNamespaceKindselector — dropSystemPlatform; offer onlyEquipment, withSimulatedreserved/hidden as it is today)
Context: With the enum value removed (Task 9) this likely already won't compile if it enumerates the value; this task makes the selector explicit and clean.
Step 1: Remove SystemPlatform from the kind dropdown / any Enum.GetValues
binding; default new namespaces to Equipment.
Step 2: Build AdminUI; commit.
blockedBy: Task 9.
Task 16: Docs — Uns.md + CLAUDE.md
Classification: small Estimated implement time: ~4 min Parallelizable with: Tasks 13, 14, 15
Files:
- Modify:
docs/Uns.md(remove the alias-tag / relay-converter sections; note Galaxy tags are ordinary equipment tags bound toGalaxyMxGateway) - Modify:
CLAUDE.md(rewrite the "Alias tags" paragraph in the Architecture section + the SystemPlatform/Galaxy notes — Galaxy is now a standard Equipment driver; noNamespaceKind.SystemPlatform, no alias/relay machinery)
Context: Keep docs honest with the new model. Do not echo any secret.
Step 1: Edit both docs. Cross-reference the design doc
docs/plans/2026-06-12-galaxy-standard-driver-design.md.
Step 2: Commit.
Task 17: Verify — build, test, user-driven live /run
Classification: verification Estimated implement time: ~5 min (agent) + user live step Parallelizable with: none
Files: none (verification)
Step 1: Full solution build + the touched test suites:
dotnet build ZB.MOM.WW.OtOpcUa.slnx
dotnet test tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests
dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests
dotnet test tests/Core/ZB.MOM.WW.OtOpcUa.Core.Tests
Expected: build 0 errors; all green.
Step 2 (USER drives — the agent does NOT sign in): Hand the user the live-verify checklist:
- Reseed a docker-dev rig: a cluster with one Equipment namespace, a
GalaxyMxGatewaydriver bound to it, one equipment tag authored via the standardTagModal+ Galaxy picker ({"FullName":"<obj>.<attr>"}). - Confirm the alias UI is gone (no "Add alias" button, no
/uns/convert-relays). - Confirm the Galaxy equipment tag shows a live value over OPC UA (Client.CLI
read/subscribe). - Confirm an existing scripted alarm on that equipment still raises/acks (native Galaxy alarms are Phase B — not expected yet).
Step 3: When the user confirms green, finish via
superpowers-extended-cc:finishing-a-development-branch (carry intent toward
merge-to-master + push, but confirm given the user-driven live step).
blockedBy: all prior tasks.
Out of scope (future plans)
- Phase B — native
IAlarmSourcealarms on the equipment-tag path (port theGenericDriverNodeManagerforwarder intoMaterialiseEquipmentTags). - Phase C — server-side
HistoryReadbackend over the Wonderware Historian reader;IsHistorized→ UAHistorizing/HistoryReadattributes; tag→tagname mapping.