The original Task 14 (5-min EF migration that "drops ConfigGeneration") was
under-scoped: the design doc (live-edit model, ~line 208) requires removing
GenerationId from 13 entities (Equipment, DriverInstance, Device, Tag,
PollGroup, Namespace, UnsArea, UnsLine, NodeAcl, Script, VirtualTag,
ScriptedAlarm) and adding RowVersion columns for last-write-wins detection.
That cascades into GenerationApplier / GenerationDiff / GenerationSealedCache
and the legacy Server/Admin CRUD services.
New decomposition (~85 min total, replacing the original 5-min estimate):
14a standard 10m Add RowVersion to live-edit entities
14b high-risk 30m Drop GenerationId FK from those entities
14c high-risk 20m Obsolete GenerationApplier/Diff/SealedCache
14d standard 5m Drop ClusterNode.RedundancyRole
14e small 5m Delete ConfigGeneration + ClusterNodeGenerationState
14f high-risk 15m Consolidator: generate V2HostingAlignment migration
Policy decision (recorded with user): OtOpcUa.Server + OtOpcUa.Admin are
allowed to fail-to-compile between 14b and Task 56 - only the new v2 projects
need to stay green. Task 56 deletes the legacy projects.
Plan markdown: replaces the original Task 14 section with the 6-task
decomposition + a header explaining the rewrite. Task index table at the
bottom of the plan updated.
Tasks JSON: replaces the single Task 14 row with 6 string-id rows
("14a", "14b", ..., "14f"). Task 15 (Migrate-To-V2.ps1) and downstream
consumers re-pointed at "14f".
Verification step in 14f rewritten to use the shared docker host at
10.100.0.35 per CLAUDE.md (Docker is not installed on this Mac dev VM).
80 lines
16 KiB
JSON
80 lines
16 KiB
JSON
{
|
|
"planPath": "docs/plans/2026-05-26-akka-hosting-alignment-plan.md",
|
|
"branch": "v2-akka-fuse",
|
|
"designDoc": "docs/plans/2026-05-26-akka-hosting-alignment-design.md",
|
|
"lastUpdated": "2026-05-26T00:00:00Z",
|
|
"tasks": [
|
|
{"id": 0, "subject": "Task 0: Create branch and central package management", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [], "commit": "2b81147"},
|
|
{"id": 1, "subject": "Task 1: Create OtOpcUa.Commons project", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [2,3,4,5,6,7,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 2, "subject": "Task 2: Create OtOpcUa.Cluster project", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [1,3,4,5,6,7,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 3, "subject": "Task 3: Create OtOpcUa.Security project", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [1,2,4,5,6,7,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 4, "subject": "Task 4: Create OtOpcUa.ControlPlane project", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [1,2,3,5,6,7,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 5, "subject": "Task 5: Create OtOpcUa.Runtime project", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [1,2,3,4,6,7,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 6, "subject": "Task 6: Create OtOpcUa.OpcUaServer project", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [1,2,3,4,5,7,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 7, "subject": "Task 7: Create OtOpcUa.AdminUI Razor class library", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [1,2,3,4,5,6,8], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 8, "subject": "Task 8: Create OtOpcUa.Host Web SDK project", "status": "completed", "classification": "small", "estMinutes": 5, "parallelizableWith": [1,2,3,4,5,6,7], "blockedBy": [0], "commit": "30a2104"},
|
|
{"id": 9, "subject": "Task 9: Build green smoke check", "status": "completed", "classification": "trivial", "estMinutes": 2, "parallelizableWith": [], "blockedBy": [1,2,3,4,5,6,7,8], "commit": "30a2104"},
|
|
{"id": 10, "subject": "Task 10: Add Deployment entity", "status": "completed", "classification": "standard", "estMinutes": 5, "parallelizableWith": [11,12,13], "blockedBy": [9], "commit": "8e2c4f2"},
|
|
{"id": 11, "subject": "Task 11: Add NodeDeploymentState entity", "status": "completed", "classification": "standard", "estMinutes": 5, "parallelizableWith": [10,12,13], "blockedBy": [9], "commit": "8e2c4f2"},
|
|
{"id": 12, "subject": "Task 12: Add ConfigEdit audit entity", "status": "completed", "classification": "small", "estMinutes": 4, "parallelizableWith": [10,11,13], "blockedBy": [9], "commit": "8e2c4f2"},
|
|
{"id": 13, "subject": "Task 13: Add DataProtection keys table", "status": "completed", "classification": "small", "estMinutes": 3, "parallelizableWith": [10,11,12], "blockedBy": [9], "commit": "8e2c4f2"},
|
|
{"id": "14a", "subject": "Task 14a: Add RowVersion to live-edit entities", "status": "pending", "classification": "standard", "estMinutes": 10, "parallelizableWith": [], "blockedBy": [13]},
|
|
{"id": "14b", "subject": "Task 14b: Decouple live-edit entities from ConfigGeneration", "status": "pending", "classification": "high-risk", "estMinutes": 30, "parallelizableWith": [], "blockedBy": ["14a"]},
|
|
{"id": "14c", "subject": "Task 14c: Obsolete GenerationApplier/Diff/SealedCache", "status": "pending", "classification": "high-risk", "estMinutes": 20, "parallelizableWith": [], "blockedBy": ["14b"]},
|
|
{"id": "14d", "subject": "Task 14d: Drop ClusterNode.RedundancyRole", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": ["14a","14b","14c"], "blockedBy": [13]},
|
|
{"id": "14e", "subject": "Task 14e: Delete ConfigGeneration + ClusterNodeGenerationState", "status": "pending", "classification": "small", "estMinutes": 5, "parallelizableWith": [], "blockedBy": ["14b","14c"]},
|
|
{"id": "14f", "subject": "Task 14f: V2HostingAlignment EF migration (consolidator)", "status": "pending", "classification": "high-risk", "estMinutes": 15, "parallelizableWith": [], "blockedBy": ["14a","14b","14c","14d","14e"]},
|
|
{"id": 15, "subject": "Task 15: Migrate-To-V2.ps1 idempotent script", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [16,17,18], "blockedBy": ["14f"]},
|
|
{"id": 16, "subject": "Task 16: Common types (CorrelationId, ExecutionId, NodeId, ...)", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [17,18], "blockedBy": [9]},
|
|
{"id": 17, "subject": "Task 17: Akka message contracts", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [16,18], "blockedBy": [16]},
|
|
{"id": 18, "subject": "Task 18: Common interfaces", "status": "pending", "classification": "small", "estMinutes": 4, "parallelizableWith": [16,17], "blockedBy": [16]},
|
|
{"id": 19, "subject": "Task 19: HOCON config", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [20,21,22], "blockedBy": [2]},
|
|
{"id": 20, "subject": "Task 20: AkkaHostedService implementation", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [19,21,22], "blockedBy": [2,18]},
|
|
{"id": 21, "subject": "Task 21: Role parser from OTOPCUA_ROLES env", "status": "pending", "classification": "small", "estMinutes": 3, "parallelizableWith": [19,20,22], "blockedBy": [2]},
|
|
{"id": 22, "subject": "Task 22: ClusterRoleInfo implementation", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [19,20,21], "blockedBy": [18,20]},
|
|
{"id": 23, "subject": "Task 23: Cluster test project + tests", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [19,20,21,22]},
|
|
{"id": 24, "subject": "Task 24: Move LdapAuthService into OtOpcUa.Security", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [25], "blockedBy": [3]},
|
|
{"id": 25, "subject": "Task 25: JwtTokenService", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [24], "blockedBy": [3]},
|
|
{"id": 26, "subject": "Task 26: Cookie+JWT hybrid AddOtOpcUaAuth extension", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [27,28], "blockedBy": [13,24,25]},
|
|
{"id": 27, "subject": "Task 27: /auth/login, /auth/ping, /auth/token endpoints", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [26,28], "blockedBy": [24,25]},
|
|
{"id": 28, "subject": "Task 28: CookieAuthenticationStateProvider for Blazor", "status": "pending", "classification": "small", "estMinutes": 4, "parallelizableWith": [26,27], "blockedBy": [25]},
|
|
{"id": 29, "subject": "Task 29: Security test project + tests", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [24,25,26,27,28]},
|
|
{"id": 30, "subject": "Task 30: ConfigPublishCoordinator happy path", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [32,33,34,35], "blockedBy": [4,17,18,10,11]},
|
|
{"id": 31, "subject": "Task 31: Coordinator timeout + failover recovery", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [32,33,34,35], "blockedBy": [30]},
|
|
{"id": 32, "subject": "Task 32: AdminOperationsActor + StartDeployment", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [30,31,33,34,35], "blockedBy": [4,17,18,10,12]},
|
|
{"id": 33, "subject": "Task 33: AuditWriterActor batched idempotent insert", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [30,31,32,34,35], "blockedBy": [4,17]},
|
|
{"id": 34, "subject": "Task 34: FleetStatusBroadcaster", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [30,31,32,33,35], "blockedBy": [4,17]},
|
|
{"id": 35, "subject": "Task 35: RedundancyStateActor + ServiceLevelCalculator", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [30,31,32,33,34], "blockedBy": [4,17,18]},
|
|
{"id": 36, "subject": "Task 36: Singleton registration extension (admin role)", "status": "pending", "classification": "standard", "estMinutes": 4, "parallelizableWith": [], "blockedBy": [30,31,32,33,34,35]},
|
|
{"id": 37, "subject": "Task 37: DriverHostActor scaffolding + PreStart recovery", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [41,42,43,44], "blockedBy": [5,17,18,11]},
|
|
{"id": 38, "subject": "Task 38: DriverHostActor DispatchDeployment handler", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [41,42,43,44], "blockedBy": [37]},
|
|
{"id": 39, "subject": "Task 39: DriverHostActor stale-config fallback", "status": "pending", "classification": "standard", "estMinutes": 4, "parallelizableWith": [41,42,43,44], "blockedBy": [38]},
|
|
{"id": 40, "subject": "Task 40: Runtime test project bootstrap", "status": "pending", "classification": "small", "estMinutes": 3, "parallelizableWith": [], "blockedBy": [37,38,39]},
|
|
{"id": 41, "subject": "Task 41: DriverInstanceActor state machine", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [42,43,44], "blockedBy": [5,17,40]},
|
|
{"id": 42, "subject": "Task 42: VirtualTagActor", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [41,43,44], "blockedBy": [5,17,40]},
|
|
{"id": 43, "subject": "Task 43: ScriptedAlarmActor", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [41,42,44], "blockedBy": [5,17,40]},
|
|
{"id": 44, "subject": "Task 44: OpcUaPublishActor on synchronized dispatcher", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [41,42,43], "blockedBy": [5,6,17,19,40]},
|
|
{"id": 45, "subject": "Task 45: HistorianAdapter + PeerOpcUaProbe + DbHealthProbe actors", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [37,40]},
|
|
{"id": 46, "subject": "Task 46: Extract OpcUaApplicationHost + Phase7Composer", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [6]},
|
|
{"id": 47, "subject": "Task 47: Phase7Composer purity + property tests", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [48,49,50,51,52], "blockedBy": [46]},
|
|
{"id": 48, "subject": "Task 48: Move Blazor components into AdminUI library", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [47], "blockedBy": [7]},
|
|
{"id": 49, "subject": "Task 49: Move SignalR hubs and rewire to FleetStatusBroadcaster", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [50,51,52], "blockedBy": [34,48]},
|
|
{"id": 50, "subject": "Task 50: IAdminOperationsClient via ClusterSingletonProxy", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [49,51,52], "blockedBy": [18,32,48]},
|
|
{"id": 51, "subject": "Task 51: Replace DriverDiagnosticsClient with IFleetDiagnosticsClient", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [49,50,52], "blockedBy": [18,48]},
|
|
{"id": 52, "subject": "Task 52: Drift indicator + Deploy button UI", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [49,50,51], "blockedBy": [50,48]},
|
|
{"id": 53, "subject": "Task 53: Host Program.cs role-gated startup", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [54,55], "blockedBy": [8,15,20,21,22,26,36,40,45,46,48,49]},
|
|
{"id": 54, "subject": "Task 54: Health endpoints + appsettings layout", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [53,55], "blockedBy": [8,22]},
|
|
{"id": 55, "subject": "Task 55: Mac dev mode + DEV-STUB drivers", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [53,54], "blockedBy": [41]},
|
|
{"id": 56, "subject": "Task 56: Delete OtOpcUa.Server + OtOpcUa.Admin projects", "status": "pending", "classification": "high-risk", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [53,54,55]},
|
|
{"id": 57, "subject": "Task 57: Build & test green check", "status": "pending", "classification": "trivial", "estMinutes": 3, "parallelizableWith": [], "blockedBy": [56]},
|
|
{"id": 58, "subject": "Task 58: 2-node integration test harness", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [57]},
|
|
{"id": 59, "subject": "Task 59: Deploy + failover integration tests", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [60], "blockedBy": [58]},
|
|
{"id": 60, "subject": "Task 60: OPC UA dual-endpoint + ServiceLevel tests", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [59], "blockedBy": [58]},
|
|
{"id": 61, "subject": "Task 61: E2E test infrastructure + GitHub Actions CI", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [], "blockedBy": [59,60]},
|
|
{"id": 62, "subject": "Task 62: Rewrite Install-Services.ps1", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [63,64,65], "blockedBy": [53]},
|
|
{"id": 63, "subject": "Task 63: Traefik config + docker-dev compose", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [62,64,65], "blockedBy": [53]},
|
|
{"id": 64, "subject": "Task 64: Update existing docs (Redundancy, ServiceHosting, security)", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [62,63,65], "blockedBy": [57]},
|
|
{"id": 65, "subject": "Task 65: New v2 docs (Architecture-v2, Cluster, ControlPlane, Runtime)", "status": "pending", "classification": "standard", "estMinutes": 5, "parallelizableWith": [62,63,64], "blockedBy": [57]}
|
|
]
|
|
}
|