diff --git a/docs/plans/2026-03-16-cluster-infrastructure-refinement-design.md b/docs/plans/2026-03-16-cluster-infrastructure-refinement-design.md index 5f6dedcb..fc28222a 100644 --- a/docs/plans/2026-03-16-cluster-infrastructure-refinement-design.md +++ b/docs/plans/2026-03-16-cluster-infrastructure-refinement-design.md @@ -2,7 +2,7 @@ **Date**: 2026-03-16 **Component**: Cluster Infrastructure (`docs/requirements/Component-ClusterInfrastructure.md`) -**Status**: Approved +**Status**: Approved — superseded in part: the keep-oldest SBR decision was replaced by the auto-down decision 2026-07-21 (`docs/plans/2026-07-21-auto-down-availability-decision.md`). ## Problem diff --git a/docs/plans/2026-03-16-communication-layer-refinement-design.md b/docs/plans/2026-03-16-communication-layer-refinement-design.md index 2db43715..363de665 100644 --- a/docs/plans/2026-03-16-communication-layer-refinement-design.md +++ b/docs/plans/2026-03-16-communication-layer-refinement-design.md @@ -2,7 +2,7 @@ **Date**: 2026-03-16 **Component**: Central–Site Communication (`docs/requirements/Component-Communication.md`) -**Status**: Approved +**Status**: Approved — transport decisions superseded by the ClusterClient→gRPC migration (`docs/plans/2026-07-22-clusterclient-to-grpc-plan.md`); the "no buffering at central" decision still stands. ## Problem diff --git a/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json b/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json index f5e19ab4..f3ead155 100644 --- a/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json +++ b/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json @@ -1,13 +1,14 @@ { "planPath": "docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", "tasks": [ - {"id": 4, "subject": "Task 1: Create SiteExternalSystemRepository", "status": "pending"}, - {"id": 5, "subject": "Task 2: Create SiteNotificationRepository", "status": "pending"}, - {"id": 6, "subject": "Task 3: Add data connections to DeployArtifactsCommand", "status": "pending"}, - {"id": 7, "subject": "Task 4: Wire site-local repositories into DI", "status": "pending", "blockedBy": [4, 5, 6]}, - {"id": 8, "subject": "Task 5: Add Deploy Artifacts button to Sites admin page", "status": "pending", "blockedBy": [9]}, - {"id": 9, "subject": "Task 6: Update ArtifactDeploymentService to include all artifact types", "status": "pending"}, - {"id": 10, "subject": "Task 7: End-to-end test", "status": "pending", "blockedBy": [7, 8, 9]} + {"id": 4, "subject": "Task 1: Create SiteExternalSystemRepository", "status": "completed"}, + {"id": 5, "subject": "Task 2: Create SiteNotificationRepository", "status": "completed", "note": "Shipped as planned, then deliberately removed 2026-07-10 (arch-review 08 §1.3 — excise vestigial site notification surface; notification delivery is central-only). The notification_lists / smtp_configurations tables survive on the site as deliberately-empty, unregistered (non-replicated) tables."}, + {"id": 6, "subject": "Task 3: Add data connections to DeployArtifactsCommand", "status": "completed"}, + {"id": 7, "subject": "Task 4: Wire site-local repositories into DI", "status": "completed", "blockedBy": [4, 5, 6]}, + {"id": 8, "subject": "Task 5: Add Deploy Artifacts button to Sites admin page", "status": "completed", "blockedBy": [9]}, + {"id": 9, "subject": "Task 6: Update ArtifactDeploymentService to include all artifact types", "status": "completed"}, + {"id": 10, "subject": "Task 7: End-to-end test", "status": "completed", "blockedBy": [7, 8, 9]} ], - "lastUpdated": "2026-03-17T16:40:00Z" + "lastUpdated": "2026-08-01T00:00:00Z" } diff --git a/docs/plans/2026-03-17-management-service-cli.md.tasks.json b/docs/plans/2026-03-17-management-service-cli.md.tasks.json index 2bae8881..bb40b78b 100644 --- a/docs/plans/2026-03-17-management-service-cli.md.tasks.json +++ b/docs/plans/2026-03-17-management-service-cli.md.tasks.json @@ -1,14 +1,15 @@ { "planPath": "docs/plans/2026-03-17-management-service-cli.md", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", "tasks": [ - {"id": 11, "subject": "Task 1: Create ManagementService project and test project", "status": "pending"}, - {"id": 12, "subject": "Task 2: Define management message contracts in Commons", "status": "pending"}, - {"id": 13, "subject": "Task 3: Implement ManagementActor", "status": "pending", "blockedBy": [11, 12]}, - {"id": 14, "subject": "Task 4: Register ManagementActor on Central with ClusterClientReceptionist", "status": "pending", "blockedBy": [13]}, - {"id": 15, "subject": "Task 5: Create CLI project with ClusterClient scaffolding", "status": "pending", "blockedBy": [12]}, - {"id": 16, "subject": "Task 6: Implement CLI command groups", "status": "pending", "blockedBy": [15]}, - {"id": 17, "subject": "Task 7: Write ManagementActor unit tests", "status": "pending", "blockedBy": [13]}, - {"id": 18, "subject": "Task 8: End-to-end integration test", "status": "pending", "blockedBy": [14, 16, 17]} + {"id": 11, "subject": "Task 1: Create ManagementService project and test project", "status": "completed"}, + {"id": 12, "subject": "Task 2: Define management message contracts in Commons", "status": "completed"}, + {"id": 13, "subject": "Task 3: Implement ManagementActor", "status": "completed", "blockedBy": [11, 12]}, + {"id": 14, "subject": "Task 4: Register ManagementActor on Central with ClusterClientReceptionist", "status": "completed", "blockedBy": [13], "note": "OBSOLETE — never built; CLI shipped HTTP-only (ManagementHttpClient), receptionist deleted in the 2026-07-22 gRPC migration. Marked completed only to close the tracker."}, + {"id": 15, "subject": "Task 5: Create CLI project with ClusterClient scaffolding", "status": "completed", "blockedBy": [12], "note": "OBSOLETE — never built; CLI shipped HTTP-only (ManagementHttpClient), receptionist deleted in the 2026-07-22 gRPC migration. The CLI project itself exists; only the ClusterClient scaffolding is obsolete."}, + {"id": 16, "subject": "Task 6: Implement CLI command groups", "status": "completed", "blockedBy": [15]}, + {"id": 17, "subject": "Task 7: Write ManagementActor unit tests", "status": "completed", "blockedBy": [13]}, + {"id": 18, "subject": "Task 8: End-to-end integration test", "status": "completed", "blockedBy": [14, 16, 17]} ], - "lastUpdated": "2026-03-17T17:00:00Z" + "lastUpdated": "2026-08-01T00:00:00Z" } diff --git a/docs/plans/2026-03-21-grpc-streaming-channel.md.tasks.json b/docs/plans/2026-03-21-grpc-streaming-channel.md.tasks.json index 825cdcea..05011b5d 100644 --- a/docs/plans/2026-03-21-grpc-streaming-channel.md.tasks.json +++ b/docs/plans/2026-03-21-grpc-streaming-channel.md.tasks.json @@ -1,18 +1,19 @@ { "planPath": "docs/plans/2026-03-21-grpc-streaming-channel.md", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code — feature shipped and later extended by the full ClusterClient→gRPC migration (docs/plans/2026-07-22-clusterclient-to-grpc-plan.md)", "tasks": [ - {"id": 0, "taskId": "1", "subject": "Task 0: Proto Definition & Stub Generation", "status": "pending"}, - {"id": 1, "taskId": "2", "subject": "Task 1: Site Config — GrpcPort in NodeOptions", "status": "pending", "blockedBy": [0]}, - {"id": 2, "taskId": "3", "subject": "Task 2: Site Entity — gRPC Address Fields", "status": "pending", "blockedBy": [0]}, - {"id": 3, "taskId": "4", "subject": "Task 3: Site-Side gRPC Server — StreamRelayActor", "status": "pending", "blockedBy": [0]}, - {"id": 4, "taskId": "5", "subject": "Task 4: Site-Side gRPC Server — SiteStreamGrpcServer", "status": "pending", "blockedBy": [3]}, - {"id": 5, "taskId": "6", "subject": "Task 5: Switch Site Host to WebApplicationBuilder + gRPC", "status": "pending", "blockedBy": [4]}, - {"id": 6, "taskId": "7", "subject": "Task 6: Central-Side gRPC Client", "status": "pending", "blockedBy": [0]}, - {"id": 7, "taskId": "8", "subject": "Task 7: Update DebugStreamBridgeActor to Use gRPC", "status": "pending", "blockedBy": [6, 5]}, - {"id": 8, "taskId": "9", "subject": "Task 8: Remove ClusterClient Streaming Path", "status": "pending", "blockedBy": [7]}, - {"id": 9, "taskId": "10", "subject": "Task 9: Docker & End-to-End Integration Test", "status": "pending", "blockedBy": [5, 1, 2]}, - {"id": 10, "taskId": "11", "subject": "Task 10: Documentation Updates", "status": "pending", "blockedBy": [9]}, - {"id": 11, "taskId": "12", "subject": "Task 11: Final Guardrail Tests", "status": "pending", "blockedBy": [9]} + {"id": 0, "taskId": "1", "subject": "Task 0: Proto Definition & Stub Generation", "status": "completed"}, + {"id": 1, "taskId": "2", "subject": "Task 1: Site Config — GrpcPort in NodeOptions", "status": "completed", "blockedBy": [0]}, + {"id": 2, "taskId": "3", "subject": "Task 2: Site Entity — gRPC Address Fields", "status": "completed", "blockedBy": [0]}, + {"id": 3, "taskId": "4", "subject": "Task 3: Site-Side gRPC Server — StreamRelayActor", "status": "completed", "blockedBy": [0]}, + {"id": 4, "taskId": "5", "subject": "Task 4: Site-Side gRPC Server — SiteStreamGrpcServer", "status": "completed", "blockedBy": [3]}, + {"id": 5, "taskId": "6", "subject": "Task 5: Switch Site Host to WebApplicationBuilder + gRPC", "status": "completed", "blockedBy": [4]}, + {"id": 6, "taskId": "7", "subject": "Task 6: Central-Side gRPC Client", "status": "completed", "blockedBy": [0]}, + {"id": 7, "taskId": "8", "subject": "Task 7: Update DebugStreamBridgeActor to Use gRPC", "status": "completed", "blockedBy": [6, 5]}, + {"id": 8, "taskId": "9", "subject": "Task 8: Remove ClusterClient Streaming Path", "status": "completed", "blockedBy": [7]}, + {"id": 9, "taskId": "10", "subject": "Task 9: Docker & End-to-End Integration Test", "status": "completed", "blockedBy": [5, 1, 2]}, + {"id": 10, "taskId": "11", "subject": "Task 10: Documentation Updates", "status": "completed", "blockedBy": [9]}, + {"id": 11, "taskId": "12", "subject": "Task 11: Final Guardrail Tests", "status": "completed", "blockedBy": [9]} ], - "lastUpdated": "2026-03-21T14:15:00Z" + "lastUpdated": "2026-08-01T00:00:00Z" } diff --git a/docs/plans/2026-03-22-primary-backup-data-connections.md.tasks.json b/docs/plans/2026-03-22-primary-backup-data-connections.md.tasks.json index 97ab1818..7d60e7ce 100644 --- a/docs/plans/2026-03-22-primary-backup-data-connections.md.tasks.json +++ b/docs/plans/2026-03-22-primary-backup-data-connections.md.tasks.json @@ -1,14 +1,15 @@ { "planPath": "docs/plans/2026-03-22-primary-backup-data-connections.md", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code — shipped (EF migration AddPrimaryBackupDataConnections; failover coverage in DataConnectionActorTests)", "tasks": [ - {"id": 1, "subject": "Task 1: Entity Model & Database Migration", "status": "pending"}, - {"id": 2, "subject": "Task 2: Update CreateConnectionCommand & Manager Actor", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 3: DataConnectionActor Failover State Machine", "status": "pending", "blockedBy": [1, 2]}, - {"id": 4, "subject": "Task 4: Failover Tests", "status": "pending", "blockedBy": [3]}, - {"id": 5, "subject": "Task 5: Health Reporting & Site Event Logging", "status": "pending", "blockedBy": [3]}, - {"id": 6, "subject": "Task 6: Central UI Changes", "status": "pending", "blockedBy": [1]}, - {"id": 7, "subject": "Task 7: CLI, Management API, and Deployment", "status": "pending", "blockedBy": [1]}, - {"id": 8, "subject": "Task 8: Documentation Updates", "status": "pending", "blockedBy": [3]} + {"id": 1, "subject": "Task 1: Entity Model & Database Migration", "status": "completed", "note": "EF migration AddPrimaryBackupDataConnections."}, + {"id": 2, "subject": "Task 2: Update CreateConnectionCommand & Manager Actor", "status": "completed", "blockedBy": [1]}, + {"id": 3, "subject": "Task 3: DataConnectionActor Failover State Machine", "status": "completed", "blockedBy": [1, 2]}, + {"id": 4, "subject": "Task 4: Failover Tests", "status": "completed", "blockedBy": [3], "note": "Covered by DataConnectionActorTests."}, + {"id": 5, "subject": "Task 5: Health Reporting & Site Event Logging", "status": "completed", "blockedBy": [3]}, + {"id": 6, "subject": "Task 6: Central UI Changes", "status": "completed", "blockedBy": [1]}, + {"id": 7, "subject": "Task 7: CLI, Management API, and Deployment", "status": "completed", "blockedBy": [1]}, + {"id": 8, "subject": "Task 8: Documentation Updates", "status": "completed", "blockedBy": [3]} ], - "lastUpdated": "2026-03-22T12:00:00Z" + "lastUpdated": "2026-08-01T00:00:00Z" } diff --git a/docs/plans/2026-03-23-treeview-component.md.tasks.json b/docs/plans/2026-03-23-treeview-component.md.tasks.json index 068c4cab..57c00d64 100644 --- a/docs/plans/2026-03-23-treeview-component.md.tasks.json +++ b/docs/plans/2026-03-23-treeview-component.md.tasks.json @@ -1,16 +1,17 @@ { "planPath": "docs/plans/2026-03-23-treeview-component.md", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", "tasks": [ - {"id": 22, "subject": "Task 1: Create TreeView.razor — Core Rendering (R1-R4, R14)", "status": "pending"}, - {"id": 23, "subject": "Task 2: Add Selection Support (R5)", "status": "pending", "blockedBy": [22]}, - {"id": 24, "subject": "Task 3: Add Session Storage Persistence (R11)", "status": "pending", "blockedBy": [23]}, - {"id": 25, "subject": "Task 4: Add ExpandAll, CollapseAll, RevealNode (R12, R13)", "status": "pending", "blockedBy": [24]}, - {"id": 26, "subject": "Task 5: Add Context Menu (R15)", "status": "pending", "blockedBy": [25]}, - {"id": 27, "subject": "Task 6: Add External Filtering Tests (R8)", "status": "pending", "blockedBy": [26]}, - {"id": 28, "subject": "Task 7: Integrate TreeView into Data Connections Page", "status": "pending", "blockedBy": [27]}, - {"id": 29, "subject": "Task 8: Integrate TreeView into Areas Page", "status": "pending", "blockedBy": [27]}, - {"id": 30, "subject": "Task 9: Integrate TreeView into Instances Page", "status": "pending", "blockedBy": [27]}, - {"id": 31, "subject": "Task 10: Full Build Verification", "status": "pending", "blockedBy": [28, 29, 30]} + {"id": 22, "subject": "Task 1: Create TreeView.razor — Core Rendering (R1-R4, R14)", "status": "completed"}, + {"id": 23, "subject": "Task 2: Add Selection Support (R5)", "status": "completed", "blockedBy": [22]}, + {"id": 24, "subject": "Task 3: Add Session Storage Persistence (R11)", "status": "completed", "blockedBy": [23]}, + {"id": 25, "subject": "Task 4: Add ExpandAll, CollapseAll, RevealNode (R12, R13)", "status": "completed", "blockedBy": [24]}, + {"id": 26, "subject": "Task 5: Add Context Menu (R15)", "status": "completed", "blockedBy": [25]}, + {"id": 27, "subject": "Task 6: Add External Filtering Tests (R8)", "status": "completed", "blockedBy": [26]}, + {"id": 28, "subject": "Task 7: Integrate TreeView into Data Connections Page", "status": "completed", "blockedBy": [27]}, + {"id": 29, "subject": "Task 8: Integrate TreeView into Areas Page", "status": "completed", "blockedBy": [27], "note": "OBSOLETE — those pages never existed; Site/Area/Instance render in one tree on Pages/Deployment/Topology.razor (2026-05-11 topology design). Marked completed only to close the tracker."}, + {"id": 30, "subject": "Task 9: Integrate TreeView into Instances Page", "status": "completed", "blockedBy": [27], "note": "OBSOLETE — those pages never existed; Site/Area/Instance render in one tree on Pages/Deployment/Topology.razor (2026-05-11 topology design). Marked completed only to close the tracker."}, + {"id": 31, "subject": "Task 10: Full Build Verification", "status": "completed", "blockedBy": [28, 29, 30]} ], - "lastUpdated": "2026-03-23T00:00:00Z" + "lastUpdated": "2026-08-01T00:00:00Z" } diff --git a/docs/plans/2026-05-11-templates-folder-hierarchy.md.tasks.json b/docs/plans/2026-05-11-templates-folder-hierarchy.md.tasks.json index 2fdb7265..5a28a00e 100644 --- a/docs/plans/2026-05-11-templates-folder-hierarchy.md.tasks.json +++ b/docs/plans/2026-05-11-templates-folder-hierarchy.md.tasks.json @@ -1,29 +1,30 @@ { "planPath": "docs/plans/2026-05-11-templates-folder-hierarchy.md", "tasks": [ - {"id": 7, "subject": "Task 0: Confirm baseline + create work branch", "status": "pending"}, - {"id": 8, "subject": "Task 1: Add TemplateFolder entity + Template.FolderId", "status": "pending", "blockedBy": [7]}, - {"id": 9, "subject": "Task 2: EF configuration for TemplateFolder + Template.FolderId", "status": "pending", "blockedBy": [8]}, - {"id": 10, "subject": "Task 3: Generate EF migration AddTemplateFolders", "status": "pending", "blockedBy": [9]}, - {"id": 11, "subject": "Task 4: Repository methods for TemplateFolder", "status": "pending", "blockedBy": [10]}, - {"id": 12, "subject": "Task 5: TemplateFolderService.CreateFolderAsync (TDD)", "status": "pending", "blockedBy": [11]}, - {"id": 13, "subject": "Task 6: TemplateFolderService.RenameFolderAsync", "status": "pending", "blockedBy": [12]}, - {"id": 14, "subject": "Task 7: TemplateFolderService.MoveFolderAsync with cycle detection", "status": "pending", "blockedBy": [13]}, - {"id": 15, "subject": "Task 8: TemplateFolderService.DeleteFolderAsync (non-empty check)", "status": "pending", "blockedBy": [14]}, - {"id": 16, "subject": "Task 9: TemplateService.MoveTemplateAsync", "status": "pending", "blockedBy": [11]}, - {"id": 17, "subject": "Task 10: DI registration for TemplateFolderService", "status": "pending", "blockedBy": [15, 16]}, - {"id": 18, "subject": "Task 11: Management command records for TemplateFolder", "status": "pending", "blockedBy": [17]}, - {"id": 19, "subject": "Task 12: ManagementActor authorization + handlers", "status": "pending", "blockedBy": [18]}, - {"id": 20, "subject": "Task 13: Templates.razor — load folders alongside templates", "status": "pending", "blockedBy": [17]}, - {"id": 21, "subject": "Task 14: Build new TmplNode tree model", "status": "pending", "blockedBy": [20]}, - {"id": 22, "subject": "Task 15: Split-pane layout + new TreeView wiring", "status": "pending", "blockedBy": [21]}, - {"id": 23, "subject": "Task 16: Per-kind context menus", "status": "pending", "blockedBy": [22]}, - {"id": 24, "subject": "Task 17: New-folder, new-template, move-template dialogs", "status": "pending", "blockedBy": [23]}, - {"id": 25, "subject": "Task 18: Drag-drop reorganization", "status": "pending", "blockedBy": [24]}, - {"id": 26, "subject": "Task 19: Deep-link reveal on load", "status": "pending", "blockedBy": [22]}, - {"id": 27, "subject": "Task 20: bUnit tests for the new page", "status": "pending", "blockedBy": [22]}, - {"id": 28, "subject": "Task 21: Documentation updates", "status": "pending", "blockedBy": [25, 26, 27]}, - {"id": 29, "subject": "Task 22: Final smoke + green-suite check", "status": "pending", "blockedBy": [25, 26, 27, 28]} + {"id": 7, "subject": "Task 0: Confirm baseline + create work branch", "status": "completed"}, + {"id": 8, "subject": "Task 1: Add TemplateFolder entity + Template.FolderId", "status": "completed", "blockedBy": [7]}, + {"id": 9, "subject": "Task 2: EF configuration for TemplateFolder + Template.FolderId", "status": "completed", "blockedBy": [8]}, + {"id": 10, "subject": "Task 3: Generate EF migration AddTemplateFolders", "status": "completed", "blockedBy": [9]}, + {"id": 11, "subject": "Task 4: Repository methods for TemplateFolder", "status": "completed", "blockedBy": [10]}, + {"id": 12, "subject": "Task 5: TemplateFolderService.CreateFolderAsync (TDD)", "status": "completed", "blockedBy": [11]}, + {"id": 13, "subject": "Task 6: TemplateFolderService.RenameFolderAsync", "status": "completed", "blockedBy": [12]}, + {"id": 14, "subject": "Task 7: TemplateFolderService.MoveFolderAsync with cycle detection", "status": "completed", "blockedBy": [13]}, + {"id": 15, "subject": "Task 8: TemplateFolderService.DeleteFolderAsync (non-empty check)", "status": "completed", "blockedBy": [14]}, + {"id": 16, "subject": "Task 9: TemplateService.MoveTemplateAsync", "status": "completed", "blockedBy": [11]}, + {"id": 17, "subject": "Task 10: DI registration for TemplateFolderService", "status": "completed", "blockedBy": [15, 16]}, + {"id": 18, "subject": "Task 11: Management command records for TemplateFolder", "status": "completed", "blockedBy": [17]}, + {"id": 19, "subject": "Task 12: ManagementActor authorization + handlers", "status": "completed", "blockedBy": [18]}, + {"id": 20, "subject": "Task 13: Templates.razor — load folders alongside templates", "status": "completed", "blockedBy": [17]}, + {"id": 21, "subject": "Task 14: Build new TmplNode tree model", "status": "completed", "blockedBy": [20]}, + {"id": 22, "subject": "Task 15: Split-pane layout + new TreeView wiring", "status": "completed", "blockedBy": [21]}, + {"id": 23, "subject": "Task 16: Per-kind context menus", "status": "completed", "blockedBy": [22]}, + {"id": 24, "subject": "Task 17: New-folder, new-template, move-template dialogs", "status": "completed", "blockedBy": [23]}, + {"id": 25, "subject": "Task 18: Drag-drop reorganization", "status": "completed", "blockedBy": [24], "notes": "DROPPED — superseded by M9 menu-based reorder (commits e3bc19c6/314c7dea) + MoveFolderDialog; no DnD anywhere in CentralUI"}, + {"id": 26, "subject": "Task 19: Deep-link reveal on load", "status": "completed", "blockedBy": [22], "notes": "OBSOLETE — /design/templates/{id} is TemplateEdit's own route; two-page split makes in-tree reveal moot"}, + {"id": 27, "subject": "Task 20: bUnit tests for the new page", "status": "completed", "blockedBy": [22]}, + {"id": 28, "subject": "Task 21: Documentation updates", "status": "completed", "blockedBy": [25, 26, 27]}, + {"id": 29, "subject": "Task 22: Final smoke + green-suite check", "status": "completed", "blockedBy": [25, 26, 27, 28]} ], - "lastUpdated": "2026-05-11" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-12-derive-on-compose-status.md b/docs/plans/2026-05-12-derive-on-compose-status.md index 7461c3f4..d6784def 100644 --- a/docs/plans/2026-05-12-derive-on-compose-status.md +++ b/docs/plans/2026-05-12-derive-on-compose-status.md @@ -1,5 +1,11 @@ # Derive-on-compose: implementation status +> **2026-08-01:** The "Still to verify" items below are satisfied — the +> Phase-3 migration ships and auto-applies, and +> `2026-05-18-contained-template-names-design.md` records it applied and +> browser-verified on the dev cluster. The "How to resume" section is +> historical. + > **For Claude resuming later:** All nine phases are implemented. This > file is the change-record for the work, not a plan. See the companion > design doc `2026-05-12-derive-on-compose-design.md` for rationale. diff --git a/docs/plans/2026-05-12-opcua-config-model.md.tasks.json b/docs/plans/2026-05-12-opcua-config-model.md.tasks.json index c8e05137..81d7a281 100644 --- a/docs/plans/2026-05-12-opcua-config-model.md.tasks.json +++ b/docs/plans/2026-05-12-opcua-config-model.md.tasks.json @@ -1,20 +1,21 @@ { "planPath": "docs/plans/2026-05-12-opcua-config-model.md", "tasks": [ - {"id": 45, "subject": "Task 1: Create OPC UA config POCOs + ValidationCategory.ConnectionConfig", "status": "pending"}, - {"id": 46, "subject": "Task 2: TDD failing tests for OpcUaEndpointConfigSerializer", "status": "pending", "blockedBy": [45]}, - {"id": 47, "subject": "Task 3: Implement OpcUaEndpointConfigSerializer", "status": "pending", "blockedBy": [46]}, - {"id": 48, "subject": "Task 4: TDD failing tests for OpcUaEndpointConfigValidator", "status": "pending", "blockedBy": [45]}, - {"id": 49, "subject": "Task 5: Implement OpcUaEndpointConfigValidator", "status": "pending", "blockedBy": [48]}, - {"id": 50, "subject": "Task 6: Refactor OpcUaDataConnection.ConnectAsync to use FromFlatDict", "status": "pending", "blockedBy": [47]}, - {"id": 51, "subject": "Task 7: Refactor DeploymentManagerActor.EnsureDclConnections", "status": "pending", "blockedBy": [47]}, - {"id": 52, "subject": "Task 8: TDD failing bUnit tests for OpcUaEndpointEditor", "status": "pending", "blockedBy": [45, 49]}, - {"id": 53, "subject": "Task 9: Implement OpcUaEndpointEditor.razor", "status": "pending", "blockedBy": [52]}, - {"id": 54, "subject": "Task 10: TDD failing bUnit tests for DataConnectionForm refactor", "status": "pending", "blockedBy": [47, 49]}, - {"id": 55, "subject": "Task 11: Refactor DataConnectionForm.razor", "status": "pending", "blockedBy": [53, 54]}, - {"id": 56, "subject": "Task 12: Solution build + all test suites green", "status": "pending", "blockedBy": [50, 51, 55]}, - {"id": 57, "subject": "Task 13: Docker deploy + browser smoke", "status": "pending", "blockedBy": [56]}, - {"id": 58, "subject": "Task 14: Push to origin", "status": "pending", "blockedBy": [57]} + {"id": 45, "subject": "Task 1: Create OPC UA config POCOs + ValidationCategory.ConnectionConfig", "status": "completed"}, + {"id": 46, "subject": "Task 2: TDD failing tests for OpcUaEndpointConfigSerializer", "status": "completed", "blockedBy": [45]}, + {"id": 47, "subject": "Task 3: Implement OpcUaEndpointConfigSerializer", "status": "completed", "blockedBy": [46]}, + {"id": 48, "subject": "Task 4: TDD failing tests for OpcUaEndpointConfigValidator", "status": "completed", "blockedBy": [45]}, + {"id": 49, "subject": "Task 5: Implement OpcUaEndpointConfigValidator", "status": "completed", "blockedBy": [48]}, + {"id": 50, "subject": "Task 6: Refactor OpcUaDataConnection.ConnectAsync to use FromFlatDict", "status": "completed", "blockedBy": [47]}, + {"id": 51, "subject": "Task 7: Refactor DeploymentManagerActor.EnsureDclConnections", "status": "completed", "blockedBy": [47]}, + {"id": 52, "subject": "Task 8: TDD failing bUnit tests for OpcUaEndpointEditor", "status": "completed", "blockedBy": [45, 49]}, + {"id": 53, "subject": "Task 9: Implement OpcUaEndpointEditor.razor", "status": "completed", "blockedBy": [52]}, + {"id": 54, "subject": "Task 10: TDD failing bUnit tests for DataConnectionForm refactor", "status": "completed", "blockedBy": [47, 49]}, + {"id": 55, "subject": "Task 11: Refactor DataConnectionForm.razor", "status": "completed", "blockedBy": [53, 54]}, + {"id": 56, "subject": "Task 12: Solution build + all test suites green", "status": "completed", "blockedBy": [50, 51, 55]}, + {"id": 57, "subject": "Task 13: Docker deploy + browser smoke", "status": "completed", "blockedBy": [56]}, + {"id": 58, "subject": "Task 14: Push to origin", "status": "completed", "blockedBy": [57]} ], - "lastUpdated": "2026-05-12T04:33:33Z" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-12-ui-audit.md b/docs/plans/2026-05-12-ui-audit.md index 1c254bc1..8f57a3c6 100644 --- a/docs/plans/2026-05-12-ui-audit.md +++ b/docs/plans/2026-05-12-ui-audit.md @@ -5,6 +5,8 @@ **Scope:** All Razor pages, layout, and shared components in `src/ZB.MOM.WW.ScadaBridge.CentralUI`. **Reference pattern:** `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Admin/Sites.razor` — 2-column responsive card grid, header flex row, kebab menus, search filter, Bootstrap collapse for noisy details, `@key=` on iterated cards, "No X match the filter." and empty-state CTAs. +> **2026-08-01: fully superseded** — every cross-cutting recommendation was either shipped or explicitly scoped into the M10 UI/UX platform plan (`2026-06-18`) and KPI History (#26); retained for reference only. + ## Constraints (recap) - Blazor Server + Bootstrap 5 only. **No third-party component frameworks** (no MudBlazor / Radzen / Blazorise / Syncfusion). diff --git a/docs/plans/2026-05-16-expression-trigger.md.tasks.json b/docs/plans/2026-05-16-expression-trigger.md.tasks.json index 6b261fcd..7e2ddb92 100644 --- a/docs/plans/2026-05-16-expression-trigger.md.tasks.json +++ b/docs/plans/2026-05-16-expression-trigger.md.tasks.json @@ -1,11 +1,12 @@ { "planPath": "docs/plans/2026-05-16-expression-trigger.md", "tasks": [ - {"id": 25, "subject": "Task 1: Trigger model + codecs", "status": "pending"}, - {"id": 26, "subject": "Task 2: Runtime expression evaluation", "status": "pending", "blockedBy": [25]}, - {"id": 27, "subject": "Task 3: Trigger editor panels", "status": "pending", "blockedBy": [25]}, - {"id": 28, "subject": "Task 4: Pre-deployment validation", "status": "pending", "blockedBy": [25, 26]}, - {"id": 29, "subject": "Task 5: Build, deploy, verify", "status": "pending", "blockedBy": [25, 26, 27, 28]} + {"id": 25, "subject": "Task 1: Trigger model + codecs", "status": "completed"}, + {"id": 26, "subject": "Task 2: Runtime expression evaluation", "status": "completed", "blockedBy": [25]}, + {"id": 27, "subject": "Task 3: Trigger editor panels", "status": "completed", "blockedBy": [25]}, + {"id": 28, "subject": "Task 4: Pre-deployment validation", "status": "completed", "blockedBy": [25, 26]}, + {"id": 29, "subject": "Task 5: Build, deploy, verify", "status": "completed", "blockedBy": [25, 26, 27, 28]} ], - "lastUpdated": "2026-05-16" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-18-notification-outbox.md.tasks.json b/docs/plans/2026-05-18-notification-outbox.md.tasks.json index 52ae744b..91b3ed3b 100644 --- a/docs/plans/2026-05-18-notification-outbox.md.tasks.json +++ b/docs/plans/2026-05-18-notification-outbox.md.tasks.json @@ -1,17 +1,18 @@ { "planPath": "docs/plans/2026-05-18-notification-outbox.md", "tasks": [ - {"id": 7, "subject": "Task 1: Create Component-NotificationOutbox.md", "status": "pending"}, - {"id": 8, "subject": "Task 2: Revise Component-NotificationService.md", "status": "pending", "blockedBy": [7]}, - {"id": 9, "subject": "Task 3: Revise Component-StoreAndForward.md", "status": "pending", "blockedBy": [7]}, - {"id": 10, "subject": "Task 4: Revise Component-HealthMonitoring.md", "status": "pending", "blockedBy": [7]}, - {"id": 11, "subject": "Task 5: Revise Component-SiteEventLogging.md", "status": "pending", "blockedBy": [7]}, - {"id": 12, "subject": "Task 6: Revise Component-Communication.md", "status": "pending", "blockedBy": [7]}, - {"id": 13, "subject": "Task 7: Revise Component-CentralUI.md", "status": "pending", "blockedBy": [7]}, - {"id": 14, "subject": "Task 8: Revise Component-ConfigurationDatabase.md and Component-Commons.md", "status": "pending", "blockedBy": [7]}, - {"id": 15, "subject": "Task 9: Update README.md", "status": "pending", "blockedBy": [7, 8, 9, 10, 11, 12, 13, 14]}, - {"id": 16, "subject": "Task 10: Update CLAUDE.md", "status": "pending", "blockedBy": [7, 8, 9, 10, 11, 12, 13, 14]}, - {"id": 17, "subject": "Task 11: Cross-reference consistency sweep", "status": "pending", "blockedBy": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]} + {"id": 7, "subject": "Task 1: Create Component-NotificationOutbox.md", "status": "completed"}, + {"id": 8, "subject": "Task 2: Revise Component-NotificationService.md", "status": "completed", "blockedBy": [7]}, + {"id": 9, "subject": "Task 3: Revise Component-StoreAndForward.md", "status": "completed", "blockedBy": [7]}, + {"id": 10, "subject": "Task 4: Revise Component-HealthMonitoring.md", "status": "completed", "blockedBy": [7]}, + {"id": 11, "subject": "Task 5: Revise Component-SiteEventLogging.md", "status": "completed", "blockedBy": [7]}, + {"id": 12, "subject": "Task 6: Revise Component-Communication.md", "status": "completed", "blockedBy": [7]}, + {"id": 13, "subject": "Task 7: Revise Component-CentralUI.md", "status": "completed", "blockedBy": [7]}, + {"id": 14, "subject": "Task 8: Revise Component-ConfigurationDatabase.md and Component-Commons.md", "status": "completed", "blockedBy": [7]}, + {"id": 15, "subject": "Task 9: Update README.md", "status": "completed", "blockedBy": [7, 8, 9, 10, 11, 12, 13, 14]}, + {"id": 16, "subject": "Task 10: Update CLAUDE.md", "status": "completed", "blockedBy": [7, 8, 9, 10, 11, 12, 13, 14]}, + {"id": 17, "subject": "Task 11: Cross-reference consistency sweep", "status": "completed", "blockedBy": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]} ], - "lastUpdated": "2026-05-18" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-19-cached-call-tracking.md.tasks.json b/docs/plans/2026-05-19-cached-call-tracking.md.tasks.json index c58bb72a..551df2f2 100644 --- a/docs/plans/2026-05-19-cached-call-tracking.md.tasks.json +++ b/docs/plans/2026-05-19-cached-call-tracking.md.tasks.json @@ -1,19 +1,20 @@ { "planPath": "docs/plans/2026-05-19-cached-call-tracking.md", "tasks": [ - {"id": 6, "subject": "Task 1: Create Site Call Audit component doc", "status": "pending"}, - {"id": 7, "subject": "Task 2: Add tracking contracts to Commons", "status": "pending", "blockedBy": [6]}, - {"id": 8, "subject": "Task 3: Update Store-and-Forward doc", "status": "pending", "blockedBy": [6, 7]}, - {"id": 9, "subject": "Task 4: Update External System Gateway doc", "status": "pending", "blockedBy": [6, 7]}, - {"id": 10, "subject": "Task 5: Update Site Runtime Script Runtime API", "status": "pending", "blockedBy": [6, 7]}, - {"id": 11, "subject": "Task 6: Update Communication doc", "status": "pending", "blockedBy": [6, 7]}, - {"id": 12, "subject": "Task 7: Update Configuration Database doc", "status": "pending", "blockedBy": [6, 7]}, - {"id": 13, "subject": "Task 8: Update Central UI doc", "status": "pending", "blockedBy": [6, 7]}, - {"id": 14, "subject": "Task 9: Update Health Monitoring doc", "status": "pending", "blockedBy": [6, 7]}, - {"id": 15, "subject": "Task 10: Note shared model in notification docs", "status": "pending", "blockedBy": [6, 7]}, - {"id": 16, "subject": "Task 11: Update README component table", "status": "pending", "blockedBy": [6]}, - {"id": 17, "subject": "Task 12: Update CLAUDE.md", "status": "pending", "blockedBy": [6]}, - {"id": 18, "subject": "Task 13: Final cross-reference consistency pass", "status": "pending", "blockedBy": [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} + {"id": 6, "subject": "Task 1: Create Site Call Audit component doc", "status": "completed"}, + {"id": 7, "subject": "Task 2: Add tracking contracts to Commons", "status": "completed", "blockedBy": [6]}, + {"id": 8, "subject": "Task 3: Update Store-and-Forward doc", "status": "completed", "blockedBy": [6, 7]}, + {"id": 9, "subject": "Task 4: Update External System Gateway doc", "status": "completed", "blockedBy": [6, 7]}, + {"id": 10, "subject": "Task 5: Update Site Runtime Script Runtime API", "status": "completed", "blockedBy": [6, 7]}, + {"id": 11, "subject": "Task 6: Update Communication doc", "status": "completed", "blockedBy": [6, 7]}, + {"id": 12, "subject": "Task 7: Update Configuration Database doc", "status": "completed", "blockedBy": [6, 7]}, + {"id": 13, "subject": "Task 8: Update Central UI doc", "status": "completed", "blockedBy": [6, 7]}, + {"id": 14, "subject": "Task 9: Update Health Monitoring doc", "status": "completed", "blockedBy": [6, 7]}, + {"id": 15, "subject": "Task 10: Note shared model in notification docs", "status": "completed", "blockedBy": [6, 7]}, + {"id": 16, "subject": "Task 11: Update README component table", "status": "completed", "blockedBy": [6]}, + {"id": 17, "subject": "Task 12: Update CLAUDE.md", "status": "completed", "blockedBy": [6]}, + {"id": 18, "subject": "Task 13: Final cross-reference consistency pass", "status": "completed", "blockedBy": [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]} ], - "lastUpdated": "2026-05-19" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-19-notification-outbox-implementation.md.tasks.json b/docs/plans/2026-05-19-notification-outbox-implementation.md.tasks.json index a789915f..50cb30bc 100644 --- a/docs/plans/2026-05-19-notification-outbox-implementation.md.tasks.json +++ b/docs/plans/2026-05-19-notification-outbox-implementation.md.tasks.json @@ -1,32 +1,33 @@ { "planPath": "docs/plans/2026-05-19-notification-outbox-implementation.md", "tasks": [ - {"id": 18, "subject": "Task 1: Notification enums", "status": "pending"}, - {"id": 19, "subject": "Task 2: Notification entity POCO", "status": "pending", "blockedBy": [18]}, - {"id": 20, "subject": "Task 3: Type field on NotificationList", "status": "pending", "blockedBy": [19]}, - {"id": 21, "subject": "Task 4: Notification EF configuration + DbSet", "status": "pending", "blockedBy": [20]}, - {"id": 22, "subject": "Task 5: NotificationOutbox repository", "status": "pending", "blockedBy": [21]}, - {"id": 23, "subject": "Task 6: EF migration AddNotificationsTable", "status": "pending", "blockedBy": [22]}, - {"id": 24, "subject": "Task 7: Site/central notification message contracts", "status": "pending", "blockedBy": [23]}, - {"id": 25, "subject": "Task 8: Outbox query/action contracts", "status": "pending", "blockedBy": [24]}, - {"id": 26, "subject": "Task 9: Scaffold ZB.MOM.WW.ScadaBridge.NotificationOutbox project", "status": "pending", "blockedBy": [25]}, - {"id": 27, "subject": "Task 10: NotificationOutboxOptions", "status": "pending", "blockedBy": [26]}, - {"id": 28, "subject": "Task 11: Delivery adapter abstraction", "status": "pending", "blockedBy": [27]}, - {"id": 29, "subject": "Task 12: Email delivery adapter", "status": "pending", "blockedBy": [28]}, - {"id": 30, "subject": "Task 13: NotificationOutboxActor ingest", "status": "pending", "blockedBy": [29]}, - {"id": 31, "subject": "Task 14: Dispatcher loop", "status": "pending", "blockedBy": [30]}, - {"id": 32, "subject": "Task 15: Query, retry, discard, KPI handlers", "status": "pending", "blockedBy": [31]}, - {"id": 33, "subject": "Task 16: Daily purge job", "status": "pending", "blockedBy": [32]}, - {"id": 34, "subject": "Task 17: AddNotificationOutbox DI extension", "status": "pending", "blockedBy": [33]}, - {"id": 35, "subject": "Task 18: Retarget site S&F notification handler to central", "status": "pending", "blockedBy": [34]}, - {"id": 36, "subject": "Task 19: Async Notify.Send + Notify.Status", "status": "pending", "blockedBy": [35]}, - {"id": 37, "subject": "Task 20: Central ingest routing", "status": "pending", "blockedBy": [36]}, - {"id": 38, "subject": "Task 21: Host registration + appsettings", "status": "pending", "blockedBy": [37]}, - {"id": 39, "subject": "Task 22: CommunicationService outbox methods", "status": "pending", "blockedBy": [38]}, - {"id": 40, "subject": "Task 23: Notification Outbox Blazor page", "status": "pending", "blockedBy": [39]}, - {"id": 41, "subject": "Task 24: Health dashboard outbox KPI tiles", "status": "pending", "blockedBy": [40]}, - {"id": 42, "subject": "Task 25: End-to-end integration test", "status": "pending", "blockedBy": [41]}, - {"id": 43, "subject": "Task 26: Full build + suite verification", "status": "pending", "blockedBy": [42]} + {"id": 18, "subject": "Task 1: Notification enums", "status": "completed"}, + {"id": 19, "subject": "Task 2: Notification entity POCO", "status": "completed", "blockedBy": [18]}, + {"id": 20, "subject": "Task 3: Type field on NotificationList", "status": "completed", "blockedBy": [19]}, + {"id": 21, "subject": "Task 4: Notification EF configuration + DbSet", "status": "completed", "blockedBy": [20]}, + {"id": 22, "subject": "Task 5: NotificationOutbox repository", "status": "completed", "blockedBy": [21]}, + {"id": 23, "subject": "Task 6: EF migration AddNotificationsTable", "status": "completed", "blockedBy": [22]}, + {"id": 24, "subject": "Task 7: Site/central notification message contracts", "status": "completed", "blockedBy": [23]}, + {"id": 25, "subject": "Task 8: Outbox query/action contracts", "status": "completed", "blockedBy": [24]}, + {"id": 26, "subject": "Task 9: Scaffold ZB.MOM.WW.ScadaBridge.NotificationOutbox project", "status": "completed", "blockedBy": [25]}, + {"id": 27, "subject": "Task 10: NotificationOutboxOptions", "status": "completed", "blockedBy": [26]}, + {"id": 28, "subject": "Task 11: Delivery adapter abstraction", "status": "completed", "blockedBy": [27]}, + {"id": 29, "subject": "Task 12: Email delivery adapter", "status": "completed", "blockedBy": [28]}, + {"id": 30, "subject": "Task 13: NotificationOutboxActor ingest", "status": "completed", "blockedBy": [29]}, + {"id": 31, "subject": "Task 14: Dispatcher loop", "status": "completed", "blockedBy": [30]}, + {"id": 32, "subject": "Task 15: Query, retry, discard, KPI handlers", "status": "completed", "blockedBy": [31]}, + {"id": 33, "subject": "Task 16: Daily purge job", "status": "completed", "blockedBy": [32]}, + {"id": 34, "subject": "Task 17: AddNotificationOutbox DI extension", "status": "completed", "blockedBy": [33]}, + {"id": 35, "subject": "Task 18: Retarget site S&F notification handler to central", "status": "completed", "blockedBy": [34]}, + {"id": 36, "subject": "Task 19: Async Notify.Send + Notify.Status", "status": "completed", "blockedBy": [35]}, + {"id": 37, "subject": "Task 20: Central ingest routing", "status": "completed", "blockedBy": [36]}, + {"id": 38, "subject": "Task 21: Host registration + appsettings", "status": "completed", "blockedBy": [37]}, + {"id": 39, "subject": "Task 22: CommunicationService outbox methods", "status": "completed", "blockedBy": [38]}, + {"id": 40, "subject": "Task 23: Notification Outbox Blazor page", "status": "completed", "blockedBy": [39]}, + {"id": 41, "subject": "Task 24: Health dashboard outbox KPI tiles", "status": "completed", "blockedBy": [40]}, + {"id": 42, "subject": "Task 25: End-to-end integration test", "status": "completed", "blockedBy": [41]}, + {"id": 43, "subject": "Task 26: Full build + suite verification", "status": "completed", "blockedBy": [42]} ], - "lastUpdated": "2026-05-19" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-20-audit-log-code-roadmap.md b/docs/plans/2026-05-20-audit-log-code-roadmap.md index 38fd804c..69b36248 100644 --- a/docs/plans/2026-05-20-audit-log-code-roadmap.md +++ b/docs/plans/2026-05-20-audit-log-code-roadmap.md @@ -23,6 +23,8 @@ > for site roles, with `NoOpSiteStreamAuditClient` retained only for central/test > composition roots; and `AuditLogQueryFilter` is now multi-value per dimension. > +> **Transport wording below is stale:** the ClusterClient-based push described throughout this roadmap was replaced by gRPC in the 2026-07-22 ClusterClient→gRPC migration, and `ClusterClientSiteAuditClient` is now `SiteCommunicationAuditClient` (commit `63c16d69`). +> > **For Claude:** REQUIRED SUB-SKILL FLOW per milestone: `brainstorming` → `writing-plans` → `subagent-driven-development`. Use `docs/requirements/Component-AuditLog.md` + `alog.md` as the spec; this document is the roadmap that sequences milestones and locks acceptance criteria for each. **M1 carries full TDD-level task detail; M2–M8 are milestone-shape detail and will be expanded into bite-sized plans by their own writing-plans pass when their turn comes.** **Goal:** Implement central component #23 Audit Log — append-only forensic + operational record across every script-trust-boundary action — into the existing ScadaBridge codebase. diff --git a/docs/plans/2026-05-20-auditlog-m1-foundation.md.tasks.json b/docs/plans/2026-05-20-auditlog-m1-foundation.md.tasks.json index e35d78e7..7cf11d7f 100644 --- a/docs/plans/2026-05-20-auditlog-m1-foundation.md.tasks.json +++ b/docs/plans/2026-05-20-auditlog-m1-foundation.md.tasks.json @@ -1,24 +1,25 @@ { "planPath": "docs/plans/2026-05-20-auditlog-m1-foundation.md", "tasks": [ - {"id": "A1", "subject": "Bundle A T1: Add audit enums (Channel, Kind, Status, ForwardState)", "status": "pending"}, - {"id": "A2", "subject": "Bundle A T2: Add AuditEvent record", "status": "pending", "blockedBy": ["A1"]}, - {"id": "A3", "subject": "Bundle A T3: Add IAuditWriter + ICentralAuditWriter", "status": "pending", "blockedBy": ["A2"]}, - {"id": "A4", "subject": "Bundle A T4: Add audit telemetry + pull message DTOs", "status": "pending", "blockedBy": ["A2"]}, - {"id": "A-rev", "subject": "Bundle A combined spec+quality review", "status": "pending", "blockedBy": ["A1", "A2", "A3", "A4"]}, - {"id": "B5", "subject": "Bundle B T5: ScadaBridgeDbContext.AuditLogs + IEntityTypeConfiguration with five named indexes", "status": "pending", "blockedBy": ["A-rev"]}, - {"id": "B-rev", "subject": "Bundle B review", "status": "pending", "blockedBy": ["B5"]}, - {"id": "C67", "subject": "Bundle C T6+T7: AddAuditLogTable migration (partition fn/scheme/table/indexes) + DB roles, with infra/mssql integration tests", "status": "pending", "blockedBy": ["B-rev"]}, - {"id": "C-rev", "subject": "Bundle C review", "status": "pending", "blockedBy": ["C67"]}, - {"id": "D8", "subject": "Bundle D T8: IAuditLogRepository + EF implementation + DI registration", "status": "pending", "blockedBy": ["C-rev"]}, - {"id": "D-rev", "subject": "Bundle D review", "status": "pending", "blockedBy": ["D8"]}, - {"id": "E10", "subject": "Bundle E T10: Scaffold src/ZB.MOM.WW.ScadaBridge.AuditLog/ project + slnx entries", "status": "pending", "blockedBy": ["D-rev"]}, - {"id": "E9", "subject": "Bundle E T9: AuditLogOptions + validator", "status": "pending", "blockedBy": ["E10"]}, - {"id": "E-rev", "subject": "Bundle E review", "status": "pending", "blockedBy": ["E10", "E9"]}, - {"id": "F11", "subject": "Bundle F T11 (controller-direct): Register ZB.MOM.WW.ScadaBridge.AuditLog in Component-Host.md + README confirm", "status": "pending", "blockedBy": ["E-rev"]}, - {"id": "FINAL-rev", "subject": "Final cross-bundle review over the whole M1 branch", "status": "pending", "blockedBy": ["F11"]}, - {"id": "MERGE", "subject": "Verify gate: full solution dotnet test green, then merge --no-ff to main", "status": "pending", "blockedBy": ["FINAL-rev"]}, - {"id": "ROADMAP", "subject": "Update downstream M2-M8 sections of roadmap with realities learned in M1", "status": "pending", "blockedBy": ["MERGE"]} + {"id": "A1", "subject": "Bundle A T1: Add audit enums (Channel, Kind, Status, ForwardState)", "status": "completed"}, + {"id": "A2", "subject": "Bundle A T2: Add AuditEvent record", "status": "completed", "blockedBy": ["A1"]}, + {"id": "A3", "subject": "Bundle A T3: Add IAuditWriter + ICentralAuditWriter", "status": "completed", "blockedBy": ["A2"]}, + {"id": "A4", "subject": "Bundle A T4: Add audit telemetry + pull message DTOs", "status": "completed", "blockedBy": ["A2"]}, + {"id": "A-rev", "subject": "Bundle A combined spec+quality review", "status": "completed", "blockedBy": ["A1", "A2", "A3", "A4"]}, + {"id": "B5", "subject": "Bundle B T5: ScadaBridgeDbContext.AuditLogs + IEntityTypeConfiguration with five named indexes", "status": "completed", "blockedBy": ["A-rev"]}, + {"id": "B-rev", "subject": "Bundle B review", "status": "completed", "blockedBy": ["B5"]}, + {"id": "C67", "subject": "Bundle C T6+T7: AddAuditLogTable migration (partition fn/scheme/table/indexes) + DB roles, with infra/mssql integration tests", "status": "completed", "blockedBy": ["B-rev"]}, + {"id": "C-rev", "subject": "Bundle C review", "status": "completed", "blockedBy": ["C67"]}, + {"id": "D8", "subject": "Bundle D T8: IAuditLogRepository + EF implementation + DI registration", "status": "completed", "blockedBy": ["C-rev"]}, + {"id": "D-rev", "subject": "Bundle D review", "status": "completed", "blockedBy": ["D8"]}, + {"id": "E10", "subject": "Bundle E T10: Scaffold src/ZB.MOM.WW.ScadaBridge.AuditLog/ project + slnx entries", "status": "completed", "blockedBy": ["D-rev"]}, + {"id": "E9", "subject": "Bundle E T9: AuditLogOptions + validator", "status": "completed", "blockedBy": ["E10"]}, + {"id": "E-rev", "subject": "Bundle E review", "status": "completed", "blockedBy": ["E10", "E9"]}, + {"id": "F11", "subject": "Bundle F T11 (controller-direct): Register ZB.MOM.WW.ScadaBridge.AuditLog in Component-Host.md + README confirm", "status": "completed", "blockedBy": ["E-rev"]}, + {"id": "FINAL-rev", "subject": "Final cross-bundle review over the whole M1 branch", "status": "completed", "blockedBy": ["F11"]}, + {"id": "MERGE", "subject": "Verify gate: full solution dotnet test green, then merge --no-ff to main", "status": "completed", "blockedBy": ["FINAL-rev"]}, + {"id": "ROADMAP", "subject": "Update downstream M2-M8 sections of roadmap with realities learned in M1", "status": "completed", "blockedBy": ["MERGE"]} ], - "lastUpdated": "2026-05-20T00:00:00Z" + "lastUpdated": "2026-08-01", + "note": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-05-20-centralized-audit-log.md.tasks.json b/docs/plans/2026-05-20-centralized-audit-log.md.tasks.json index 9b5ecac4..954ef0de 100644 --- a/docs/plans/2026-05-20-centralized-audit-log.md.tasks.json +++ b/docs/plans/2026-05-20-centralized-audit-log.md.tasks.json @@ -3,24 +3,25 @@ "spec": "alog.md (commit fec0bb1)", "repoNature": "design-documentation-only", "tasks": [ - {"id": 0, "subject": "Task 0: Prepare branch", "status": "pending", "blockedBy": []}, - {"id": 1, "subject": "Task 1: Author Component-AuditLog.md", "status": "pending", "blockedBy": [0]}, - {"id": 2, "subject": "Task 2: Update Component-Commons.md", "status": "pending", "blockedBy": [0]}, - {"id": 3, "subject": "Task 3: Update Component-ConfigurationDatabase.md", "status": "pending", "blockedBy": [1]}, - {"id": 4, "subject": "Task 4: Update Component-ClusterInfrastructure.md", "status": "pending", "blockedBy": [1]}, - {"id": 5, "subject": "Task 5: Update Component-SiteRuntime.md", "status": "pending", "blockedBy": [1]}, - {"id": 6, "subject": "Task 6: Update Component-ExternalSystemGateway.md", "status": "pending", "blockedBy": [1]}, - {"id": 7, "subject": "Task 7: Update Component-SiteCallAudit.md", "status": "pending", "blockedBy": [1]}, - {"id": 8, "subject": "Task 8: Update Component-NotificationOutbox.md", "status": "pending", "blockedBy": [1]}, - {"id": 9, "subject": "Task 9: Update Component-InboundAPI.md", "status": "pending", "blockedBy": [1]}, - {"id": 10, "subject": "Task 10: Update Component-CentralUI.md", "status": "pending", "blockedBy": [1]}, - {"id": 11, "subject": "Task 11: Update Component-HealthMonitoring.md", "status": "pending", "blockedBy": [1]}, - {"id": 12, "subject": "Task 12: Update Component-CLI.md", "status": "pending", "blockedBy": [1]}, - {"id": 13, "subject": "Task 13: Update README.md", "status": "pending", "blockedBy": [1]}, - {"id": 14, "subject": "Task 14: Update HighLevelReqs.md", "status": "pending", "blockedBy": [1]}, - {"id": 15, "subject": "Task 15: Update CLAUDE.md", "status": "pending", "blockedBy": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]}, - {"id": 16, "subject": "Task 16: Final cross-reference verification", "status": "pending", "blockedBy": [15]}, - {"id": 17, "subject": "Task 17: Merge to main (user-gated)", "status": "pending", "blockedBy": [16]} + {"id": 0, "subject": "Task 0: Prepare branch", "status": "completed", "blockedBy": []}, + {"id": 1, "subject": "Task 1: Author Component-AuditLog.md", "status": "completed", "blockedBy": [0]}, + {"id": 2, "subject": "Task 2: Update Component-Commons.md", "status": "completed", "blockedBy": [0]}, + {"id": 3, "subject": "Task 3: Update Component-ConfigurationDatabase.md", "status": "completed", "blockedBy": [1]}, + {"id": 4, "subject": "Task 4: Update Component-ClusterInfrastructure.md", "status": "completed", "blockedBy": [1]}, + {"id": 5, "subject": "Task 5: Update Component-SiteRuntime.md", "status": "completed", "blockedBy": [1]}, + {"id": 6, "subject": "Task 6: Update Component-ExternalSystemGateway.md", "status": "completed", "blockedBy": [1]}, + {"id": 7, "subject": "Task 7: Update Component-SiteCallAudit.md", "status": "completed", "blockedBy": [1]}, + {"id": 8, "subject": "Task 8: Update Component-NotificationOutbox.md", "status": "completed", "blockedBy": [1]}, + {"id": 9, "subject": "Task 9: Update Component-InboundAPI.md", "status": "completed", "blockedBy": [1]}, + {"id": 10, "subject": "Task 10: Update Component-CentralUI.md", "status": "completed", "blockedBy": [1]}, + {"id": 11, "subject": "Task 11: Update Component-HealthMonitoring.md", "status": "completed", "blockedBy": [1]}, + {"id": 12, "subject": "Task 12: Update Component-CLI.md", "status": "completed", "blockedBy": [1]}, + {"id": 13, "subject": "Task 13: Update README.md", "status": "completed", "blockedBy": [1]}, + {"id": 14, "subject": "Task 14: Update HighLevelReqs.md", "status": "completed", "blockedBy": [1]}, + {"id": 15, "subject": "Task 15: Update CLAUDE.md", "status": "completed", "blockedBy": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]}, + {"id": 16, "subject": "Task 16: Final cross-reference verification", "status": "completed", "blockedBy": [15]}, + {"id": 17, "subject": "Task 17: Merge to main (user-gated)", "status": "completed", "blockedBy": [16]} ], - "lastUpdated": "2026-05-20T00:00:00Z" + "lastUpdated": "2026-08-01", + "note": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-05-21-audit-executionid.md.tasks.json b/docs/plans/2026-05-21-audit-executionid.md.tasks.json index 2db0c8ed..1a812922 100644 --- a/docs/plans/2026-05-21-audit-executionid.md.tasks.json +++ b/docs/plans/2026-05-21-audit-executionid.md.tasks.json @@ -1,16 +1,17 @@ { "planPath": "docs/plans/2026-05-21-audit-executionid.md", "tasks": [ - {"id": 50, "subject": "Task 0: Prep — verify branch + baseline", "status": "pending"}, - {"id": 51, "subject": "Task 1: Foundation — AuditEvent.ExecutionId + central AuditLog column + repo query", "status": "pending", "blockedBy": [50]}, - {"id": 52, "subject": "Task 2: Foundation — site SQLite + gRPC DTO", "status": "pending", "blockedBy": [51]}, - {"id": 53, "subject": "Task 3: Site script-side emitters stamp ExecutionId", "status": "pending", "blockedBy": [52]}, - {"id": 54, "subject": "Task 4: Cached S&F retry-loop rows carry ExecutionId", "status": "pending", "blockedBy": [53]}, - {"id": 55, "subject": "Task 5: Central NotifyDeliver rows carry ExecutionId", "status": "pending", "blockedBy": [52]}, - {"id": 56, "subject": "Task 6: Inbound audit rows carry ExecutionId", "status": "pending", "blockedBy": [52]}, - {"id": 57, "subject": "Task 7: Central UI — ExecutionId column, filter, drill-in", "status": "pending", "blockedBy": [51]}, - {"id": 58, "subject": "Task 8: CLI + ManagementService — ExecutionId filter", "status": "pending", "blockedBy": [51]}, - {"id": 59, "subject": "Task 9: End-to-end integration test + docs", "status": "pending", "blockedBy": [53, 54, 55, 56, 57, 58]} + {"id": 50, "subject": "Task 0: Prep — verify branch + baseline", "status": "completed"}, + {"id": 51, "subject": "Task 1: Foundation — AuditEvent.ExecutionId + central AuditLog column + repo query", "status": "completed", "blockedBy": [50]}, + {"id": 52, "subject": "Task 2: Foundation — site SQLite + gRPC DTO", "status": "completed", "blockedBy": [51]}, + {"id": 53, "subject": "Task 3: Site script-side emitters stamp ExecutionId", "status": "completed", "blockedBy": [52]}, + {"id": 54, "subject": "Task 4: Cached S&F retry-loop rows carry ExecutionId", "status": "completed", "blockedBy": [53]}, + {"id": 55, "subject": "Task 5: Central NotifyDeliver rows carry ExecutionId", "status": "completed", "blockedBy": [52]}, + {"id": 56, "subject": "Task 6: Inbound audit rows carry ExecutionId", "status": "completed", "blockedBy": [52]}, + {"id": 57, "subject": "Task 7: Central UI — ExecutionId column, filter, drill-in", "status": "completed", "blockedBy": [51]}, + {"id": 58, "subject": "Task 8: CLI + ManagementService — ExecutionId filter", "status": "completed", "blockedBy": [51]}, + {"id": 59, "subject": "Task 9: End-to-end integration test + docs", "status": "completed", "blockedBy": [53, 54, 55, 56, 57, 58]} ], - "lastUpdated": "2026-05-21T00:00:00Z" + "lastUpdated": "2026-08-01", + "note": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-05-21-audit-parent-executionid.md.tasks.json b/docs/plans/2026-05-21-audit-parent-executionid.md.tasks.json index 7f7b49b6..40ae05d9 100644 --- a/docs/plans/2026-05-21-audit-parent-executionid.md.tasks.json +++ b/docs/plans/2026-05-21-audit-parent-executionid.md.tasks.json @@ -1,19 +1,20 @@ { "planPath": "docs/plans/2026-05-21-audit-parent-executionid.md", "tasks": [ - {"id": 0, "subject": "Task 0: Prep — verify branch + baseline", "status": "pending"}, - {"id": 1, "subject": "Task 1: Foundation — AuditEvent.ParentExecutionId + central AuditLog column", "status": "pending", "blockedBy": [0]}, - {"id": 2, "subject": "Task 2: Foundation — site SQLite + gRPC DTO", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 3: Inbound request id minting + RouteToCallRequest.ParentExecutionId", "status": "pending", "blockedBy": [0]}, - {"id": 4, "subject": "Task 4: Thread ParentExecutionId into routed script ScriptRuntimeContext", "status": "pending", "blockedBy": [3]}, - {"id": 5, "subject": "Task 5: Site script-side emitters stamp ParentExecutionId", "status": "pending", "blockedBy": [4, 2]}, - {"id": 6, "subject": "Task 6: Cached S&F retry-loop rows carry ParentExecutionId", "status": "pending", "blockedBy": [5]}, - {"id": 7, "subject": "Task 7: Central NotifyDeliver rows carry ParentExecutionId", "status": "pending", "blockedBy": [5, 1]}, - {"id": 8, "subject": "Task 8: Repository — GetExecutionTreeAsync", "status": "pending", "blockedBy": [1]}, - {"id": 9, "subject": "Task 9: Central UI — ParentExecutionId column, filter, parent drill-in", "status": "pending", "blockedBy": [1]}, - {"id": 10, "subject": "Task 10: Central UI — execution-chain tree view", "status": "pending", "blockedBy": [8, 9]}, - {"id": 11, "subject": "Task 11: CLI + ManagementService — ParentExecutionId filter", "status": "pending", "blockedBy": [1]}, - {"id": 12, "subject": "Task 12: End-to-end integration test + docs", "status": "pending", "blockedBy": [5, 6, 7, 10, 11]} + {"id": 0, "subject": "Task 0: Prep — verify branch + baseline", "status": "completed"}, + {"id": 1, "subject": "Task 1: Foundation — AuditEvent.ParentExecutionId + central AuditLog column", "status": "completed", "blockedBy": [0]}, + {"id": 2, "subject": "Task 2: Foundation — site SQLite + gRPC DTO", "status": "completed", "blockedBy": [1]}, + {"id": 3, "subject": "Task 3: Inbound request id minting + RouteToCallRequest.ParentExecutionId", "status": "completed", "blockedBy": [0]}, + {"id": 4, "subject": "Task 4: Thread ParentExecutionId into routed script ScriptRuntimeContext", "status": "completed", "blockedBy": [3]}, + {"id": 5, "subject": "Task 5: Site script-side emitters stamp ParentExecutionId", "status": "completed", "blockedBy": [4, 2]}, + {"id": 6, "subject": "Task 6: Cached S&F retry-loop rows carry ParentExecutionId", "status": "completed", "blockedBy": [5]}, + {"id": 7, "subject": "Task 7: Central NotifyDeliver rows carry ParentExecutionId", "status": "completed", "blockedBy": [5, 1]}, + {"id": 8, "subject": "Task 8: Repository — GetExecutionTreeAsync", "status": "completed", "blockedBy": [1]}, + {"id": 9, "subject": "Task 9: Central UI — ParentExecutionId column, filter, parent drill-in", "status": "completed", "blockedBy": [1]}, + {"id": 10, "subject": "Task 10: Central UI — execution-chain tree view", "status": "completed", "blockedBy": [8, 9]}, + {"id": 11, "subject": "Task 11: CLI + ManagementService — ParentExecutionId filter", "status": "completed", "blockedBy": [1]}, + {"id": 12, "subject": "Task 12: End-to-end integration test + docs", "status": "completed", "blockedBy": [5, 6, 7, 10, 11]} ], - "lastUpdated": "2026-05-21" + "lastUpdated": "2026-08-01", + "note": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-05-23-audit-source-node.md b/docs/plans/2026-05-23-audit-source-node.md index 851eb9e9..aa7ffb4f 100644 --- a/docs/plans/2026-05-23-audit-source-node.md +++ b/docs/plans/2026-05-23-audit-source-node.md @@ -929,12 +929,12 @@ git commit -m "chore(audit): smoke-verify SourceNode end-to-end across cluster" ## Acceptance Criteria (the whole-plan checklist) -- [ ] Every audit row written from this commit forward carries `SourceNode` populated (`central-a/b` for central direct-write, `node-a/b` for site rows). -- [ ] Every new `Notifications` and `SiteCalls` row carries `SourceNode` from the site. -- [ ] `IX_AuditLog_Node_Occurred` exists in central MS SQL. -- [ ] Site SQLite `AuditLog` and `OperationTracking` tables both have `SourceNode TEXT NULL`; existing site DBs are upgraded idempotently on startup. -- [ ] Proto `AuditEventDto.source_node = 22` and `SiteCallOperationalDto.source_node = 12` exist; no field numbers reused. -- [ ] Central UI Audit Log, Notifications, and Site Calls pages all display a "Node" column and support filtering by it. -- [ ] All test projects green. -- [ ] Cluster comes up clean via `bash docker/deploy.sh`; CLI smoke confirms expected node names land in the central tables. -- [ ] Design docs (already committed in Task 0) match the implementation. +- [x] Every audit row written from this commit forward carries `SourceNode` populated (`central-a/b` for central direct-write, `node-a/b` for site rows). +- [x] Every new `Notifications` and `SiteCalls` row carries `SourceNode` from the site. +- [x] `IX_AuditLog_Node_Occurred` exists in central MS SQL. +- [x] Site SQLite `AuditLog` and `OperationTracking` tables both have `SourceNode TEXT NULL`; existing site DBs are upgraded idempotently on startup. +- [x] Proto `AuditEventDto.source_node = 22` and `SiteCallOperationalDto.source_node = 12` exist; no field numbers reused. +- [x] Central UI Audit Log, Notifications, and Site Calls pages all display a "Node" column and support filtering by it. +- [x] All test projects green. +- [x] Cluster comes up clean via `bash docker/deploy.sh`; CLI smoke confirms expected node names land in the central tables. (`NodeName` is now startup-validated — an empty value fails fast rather than NULLing `SourceNode`, commit `4a0462e4` — and the rig has redeployed many times since.) +- [x] Design docs (already committed in Task 0) match the implementation. diff --git a/docs/plans/2026-05-23-audit-source-node.md.tasks.json b/docs/plans/2026-05-23-audit-source-node.md.tasks.json index 0d6db80b..3931f97b 100644 --- a/docs/plans/2026-05-23-audit-source-node.md.tasks.json +++ b/docs/plans/2026-05-23-audit-source-node.md.tasks.json @@ -1,27 +1,28 @@ { "planPath": "docs/plans/2026-05-23-audit-source-node.md", "tasks": [ - {"id": 1, "subject": "Task 0: Branch + Snapshot", "status": "pending"}, - {"id": 2, "subject": "Task 1: NodeOptions.NodeName + INodeIdentityProvider", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 2: Add SourceNode to AuditEvent record", "status": "pending", "blockedBy": [2]}, - {"id": 4, "subject": "Task 3: Add SourceNode to SiteCallOperational + SiteCall entity", "status": "pending", "blockedBy": [2]}, - {"id": 5, "subject": "Task 4: Add SourceNode to Notification entity + NotificationSubmit", "status": "pending", "blockedBy": [2]}, - {"id": 6, "subject": "Task 5: Add source_node to proto + update DTO mappers", "status": "pending", "blockedBy": [3, 4]}, - {"id": 7, "subject": "Task 6: EF migration — SourceNode on AuditLog + IX_AuditLog_Node_Occurred", "status": "pending", "blockedBy": [3]}, - {"id": 8, "subject": "Task 7: EF migration — SourceNode on Notifications", "status": "pending", "blockedBy": [5]}, - {"id": 9, "subject": "Task 8: EF migration — SourceNode on SiteCalls", "status": "pending", "blockedBy": [4]}, - {"id": 10, "subject": "Task 9: Site SQLite AuditLog — add SourceNode (idempotent upgrade)", "status": "pending", "blockedBy": [3]}, - {"id": 11, "subject": "Task 10: Site SQLite OperationTracking — add SourceNode", "status": "pending", "blockedBy": [4]}, - {"id": 12, "subject": "Task 11: Stamp SourceNode at site SqliteAuditWriter", "status": "pending", "blockedBy": [2, 10]}, - {"id": 13, "subject": "Task 12: Stamp SourceNode at CentralAuditWriter + persist via repo", "status": "pending", "blockedBy": [2, 7]}, - {"id": 14, "subject": "Task 13: Carry SourceNode through Notifications S&F handoff", "status": "pending", "blockedBy": [2, 5, 8]}, - {"id": 15, "subject": "Task 14: Carry SourceNode through cached-call telemetry → SiteCalls", "status": "pending", "blockedBy": [2, 9, 11]}, - {"id": 16, "subject": "Task 15: UI — Node column + filter on AuditLog grid", "status": "pending", "blockedBy": [7]}, - {"id": 17, "subject": "Task 16: UI — Node column + filter on Notifications grid", "status": "pending", "blockedBy": [7]}, - {"id": 18, "subject": "Task 17: UI — Node column + filter on SiteCalls grid", "status": "pending", "blockedBy": [7]}, - {"id": 19, "subject": "Task 18: Docker appsettings — NodeName on all 8 nodes", "status": "pending", "blockedBy": [2]}, - {"id": 20, "subject": "Task 19: Full build + targeted test sweep", "status": "pending", "blockedBy": [12, 13, 14, 15, 16, 17, 18, 19]}, - {"id": 21, "subject": "Task 20: Docker redeploy + smoke verify", "status": "pending", "blockedBy": [20]} + {"id": 1, "subject": "Task 0: Branch + Snapshot", "status": "completed"}, + {"id": 2, "subject": "Task 1: NodeOptions.NodeName + INodeIdentityProvider", "status": "completed", "blockedBy": [1]}, + {"id": 3, "subject": "Task 2: Add SourceNode to AuditEvent record", "status": "completed", "blockedBy": [2]}, + {"id": 4, "subject": "Task 3: Add SourceNode to SiteCallOperational + SiteCall entity", "status": "completed", "blockedBy": [2]}, + {"id": 5, "subject": "Task 4: Add SourceNode to Notification entity + NotificationSubmit", "status": "completed", "blockedBy": [2]}, + {"id": 6, "subject": "Task 5: Add source_node to proto + update DTO mappers", "status": "completed", "blockedBy": [3, 4]}, + {"id": 7, "subject": "Task 6: EF migration — SourceNode on AuditLog + IX_AuditLog_Node_Occurred", "status": "completed", "blockedBy": [3]}, + {"id": 8, "subject": "Task 7: EF migration — SourceNode on Notifications", "status": "completed", "blockedBy": [5]}, + {"id": 9, "subject": "Task 8: EF migration — SourceNode on SiteCalls", "status": "completed", "blockedBy": [4]}, + {"id": 10, "subject": "Task 9: Site SQLite AuditLog — add SourceNode (idempotent upgrade)", "status": "completed", "blockedBy": [3]}, + {"id": 11, "subject": "Task 10: Site SQLite OperationTracking — add SourceNode", "status": "completed", "blockedBy": [4]}, + {"id": 12, "subject": "Task 11: Stamp SourceNode at site SqliteAuditWriter", "status": "completed", "blockedBy": [2, 10]}, + {"id": 13, "subject": "Task 12: Stamp SourceNode at CentralAuditWriter + persist via repo", "status": "completed", "blockedBy": [2, 7]}, + {"id": 14, "subject": "Task 13: Carry SourceNode through Notifications S&F handoff", "status": "completed", "blockedBy": [2, 5, 8]}, + {"id": 15, "subject": "Task 14: Carry SourceNode through cached-call telemetry → SiteCalls", "status": "completed", "blockedBy": [2, 9, 11]}, + {"id": 16, "subject": "Task 15: UI — Node column + filter on AuditLog grid", "status": "completed", "blockedBy": [7]}, + {"id": 17, "subject": "Task 16: UI — Node column + filter on Notifications grid", "status": "completed", "blockedBy": [7]}, + {"id": 18, "subject": "Task 17: UI — Node column + filter on SiteCalls grid", "status": "completed", "blockedBy": [7]}, + {"id": 19, "subject": "Task 18: Docker appsettings — NodeName on all 8 nodes", "status": "completed", "blockedBy": [2]}, + {"id": 20, "subject": "Task 19: Full build + targeted test sweep", "status": "completed", "blockedBy": [12, 13, 14, 15, 16, 17, 18, 19]}, + {"id": 21, "subject": "Task 20: Docker redeploy + smoke verify", "status": "completed", "blockedBy": [20]} ], - "lastUpdated": "2026-05-23T00:00:00Z" + "lastUpdated": "2026-08-01", + "note": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-05-23-inbound-api-full-response-audit-design.md b/docs/plans/2026-05-23-inbound-api-full-response-audit-design.md index 6fb7c55f..8e27a8be 100644 --- a/docs/plans/2026-05-23-inbound-api-full-response-audit-design.md +++ b/docs/plans/2026-05-23-inbound-api-full-response-audit-design.md @@ -132,17 +132,17 @@ same "options validation" path used for other AuditLog settings. ## Acceptance Criteria -- [ ] `AuditLog:InboundMaxBytes` option exists on the AuditLog options class, +- [x] `AuditLog:InboundMaxBytes` option exists on the AuditLog options class, with the documented default and bounds, validated at startup. -- [ ] Inbound request middleware writes `RequestSummary` and `ResponseSummary` +- [x] Inbound request middleware writes `RequestSummary` and `ResponseSummary` using the inbound ceiling instead of the 8 KB / 64 KB defaults. -- [ ] Other channels' rows (e.g. an `ApiOutbound.ApiCall` over the limit) still +- [x] Other channels' rows (e.g. an `ApiOutbound.ApiCall` over the limit) still truncate at 8 KB (64 KB on error rows) — regression-tested. -- [ ] `PayloadTruncated = 1` on an inbound row iff request body or response +- [x] `PayloadTruncated = 1` on an inbound row iff request body or response body exceeded `InboundMaxBytes`. -- [ ] Header redaction list and per-target body redactors still apply to +- [x] Header redaction list and per-target body redactors still apply to inbound rows. -- [ ] Redactor failure on an inbound row still produces `` and increments `AuditRedactionFailure`. -- [ ] `Component-AuditLog.md` and `Component-InboundAPI.md` updated as +- [x] `Component-AuditLog.md` and `Component-InboundAPI.md` updated as described in **Doc Edits**. diff --git a/docs/plans/2026-05-23-inbound-api-full-response-audit.md.tasks.json b/docs/plans/2026-05-23-inbound-api-full-response-audit.md.tasks.json index 070f7ade..685ebd44 100644 --- a/docs/plans/2026-05-23-inbound-api-full-response-audit.md.tasks.json +++ b/docs/plans/2026-05-23-inbound-api-full-response-audit.md.tasks.json @@ -1,13 +1,14 @@ { "planPath": "docs/plans/2026-05-23-inbound-api-full-response-audit.md", "tasks": [ - {"id": 1, "subject": "Task 0: Prep — branch, baseline build", "status": "pending"}, - {"id": 2, "subject": "Task 1: Add InboundMaxBytes to AuditLogOptions (TDD)", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 2: Wire InboundMaxBytes into DefaultAuditPayloadFilter (TDD)", "status": "pending", "blockedBy": [2]}, - {"id": 4, "subject": "Task 3: Capture response body in AuditWriteMiddleware (TDD)", "status": "pending", "blockedBy": [3]}, - {"id": 5, "subject": "Task 4: Update Component-AuditLog.md", "status": "pending", "blockedBy": [4]}, - {"id": 6, "subject": "Task 5: Update Component-InboundAPI.md", "status": "pending", "blockedBy": [5]}, - {"id": 7, "subject": "Task 6: Final build + full test run + branch summary", "status": "pending", "blockedBy": [6]} + {"id": 1, "subject": "Task 0: Prep — branch, baseline build", "status": "completed"}, + {"id": 2, "subject": "Task 1: Add InboundMaxBytes to AuditLogOptions (TDD)", "status": "completed", "blockedBy": [1]}, + {"id": 3, "subject": "Task 2: Wire InboundMaxBytes into DefaultAuditPayloadFilter (TDD)", "status": "completed", "blockedBy": [2]}, + {"id": 4, "subject": "Task 3: Capture response body in AuditWriteMiddleware (TDD)", "status": "completed", "blockedBy": [3]}, + {"id": 5, "subject": "Task 4: Update Component-AuditLog.md", "status": "completed", "blockedBy": [4]}, + {"id": 6, "subject": "Task 5: Update Component-InboundAPI.md", "status": "completed", "blockedBy": [5]}, + {"id": 7, "subject": "Task 6: Final build + full test run + branch summary", "status": "completed", "blockedBy": [6]} ], - "lastUpdated": "2026-05-23" + "lastUpdated": "2026-08-01", + "note": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-05-28-opcua-tag-browser.md.tasks.json b/docs/plans/2026-05-28-opcua-tag-browser.md.tasks.json index 4b63fcac..44f902ee 100644 --- a/docs/plans/2026-05-28-opcua-tag-browser.md.tasks.json +++ b/docs/plans/2026-05-28-opcua-tag-browser.md.tasks.json @@ -1,28 +1,29 @@ { "planPath": "docs/plans/2026-05-28-opcua-tag-browser.md", "tasks": [ - {"id": 70, "subject": "Task 1: Add DataSourceReferenceOverride to InstanceConnectionBinding entity", "status": "pending"}, - {"id": 71, "subject": "Task 2: Add override to ConnectionBinding wire record + ManagementActor mapping", "status": "pending"}, - {"id": 72, "subject": "Task 3: EF mapping for DataSourceReferenceOverride column", "status": "pending"}, - {"id": 73, "subject": "Task 4: EF Core migration AddInstanceConnectionBindingOverride", "status": "pending", "blockedBy": [72]}, - {"id": 74, "subject": "Task 5: IBrowsableDataConnection interface + BrowseNode types", "status": "pending"}, - {"id": 75, "subject": "Task 6: BrowseCommands.cs (BrowseOpcUaNodeCommand + result + failure)", "status": "pending"}, - {"id": 76, "subject": "Task 7: Add BrowseChildrenAsync to IOpcUaClient", "status": "pending", "blockedBy": [74, 75]}, - {"id": 77, "subject": "Task 8: Implement BrowseChildrenAsync on RealOpcUaClient", "status": "pending", "blockedBy": [76]}, - {"id": 78, "subject": "Task 9: Implement IBrowsableDataConnection on OpcUaDataConnection", "status": "pending", "blockedBy": [76]}, - {"id": 79, "subject": "Task 10: Handle BrowseOpcUaNodeCommand in DataConnectionManagerActor", "status": "pending", "blockedBy": [75, 76]}, - {"id": 80, "subject": "Task 11: Forward BrowseOpcUaNodeCommand in SiteCommunicationActor", "status": "pending", "blockedBy": [79]}, - {"id": 81, "subject": "Task 12: Apply override in FlatteningService.ApplyConnectionBindings", "status": "pending", "blockedBy": [70]}, - {"id": 82, "subject": "Task 13: Revision-hash regression test", "status": "pending", "blockedBy": [81]}, - {"id": 83, "subject": "Task 14: IOpcUaBrowseService + impl + DI registration", "status": "pending", "blockedBy": [75]}, - {"id": 84, "subject": "Task 15: Scaffold OpcUaBrowserDialog modal", "status": "pending"}, - {"id": 85, "subject": "Task 16: Tree rendering + lazy load + selection in dialog", "status": "pending", "blockedBy": [83, 84]}, - {"id": 86, "subject": "Task 17: Error banner mapping + polish on dialog", "status": "pending", "blockedBy": [85]}, - {"id": 87, "subject": "Task 18: Add Override column + Browse button to InstanceConfigure.razor", "status": "pending", "blockedBy": [83, 86]}, - {"id": 88, "subject": "Task 19: End-to-end manual smoke (online + offline)", "status": "pending", "blockedBy": [70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87]}, - {"id": 89, "subject": "Task 20: Update Component-DataConnectionLayer.md", "status": "pending", "blockedBy": [81]}, - {"id": 90, "subject": "Task 21: Update Component-TemplateEngine.md", "status": "pending", "blockedBy": [81]}, - {"id": 91, "subject": "Task 22: Update Component-CentralUI.md", "status": "pending", "blockedBy": [87]} + {"id": 70, "subject": "Task 1: Add DataSourceReferenceOverride to InstanceConnectionBinding entity", "status": "completed"}, + {"id": 71, "subject": "Task 2: Add override to ConnectionBinding wire record + ManagementActor mapping", "status": "completed"}, + {"id": 72, "subject": "Task 3: EF mapping for DataSourceReferenceOverride column", "status": "completed"}, + {"id": 73, "subject": "Task 4: EF Core migration AddInstanceConnectionBindingOverride", "status": "completed", "blockedBy": [72]}, + {"id": 74, "subject": "Task 5: IBrowsableDataConnection interface + BrowseNode types", "status": "completed"}, + {"id": 75, "subject": "Task 6: BrowseCommands.cs (BrowseOpcUaNodeCommand + result + failure)", "status": "completed"}, + {"id": 76, "subject": "Task 7: Add BrowseChildrenAsync to IOpcUaClient", "status": "completed", "blockedBy": [74, 75]}, + {"id": 77, "subject": "Task 8: Implement BrowseChildrenAsync on RealOpcUaClient", "status": "completed", "blockedBy": [76]}, + {"id": 78, "subject": "Task 9: Implement IBrowsableDataConnection on OpcUaDataConnection", "status": "completed", "blockedBy": [76]}, + {"id": 79, "subject": "Task 10: Handle BrowseOpcUaNodeCommand in DataConnectionManagerActor", "status": "completed", "blockedBy": [75, 76]}, + {"id": 80, "subject": "Task 11: Forward BrowseOpcUaNodeCommand in SiteCommunicationActor", "status": "completed", "blockedBy": [79]}, + {"id": 81, "subject": "Task 12: Apply override in FlatteningService.ApplyConnectionBindings", "status": "completed", "blockedBy": [70]}, + {"id": 82, "subject": "Task 13: Revision-hash regression test", "status": "completed", "blockedBy": [81]}, + {"id": 83, "subject": "Task 14: IOpcUaBrowseService + impl + DI registration", "status": "completed", "blockedBy": [75]}, + {"id": 84, "subject": "Task 15: Scaffold OpcUaBrowserDialog modal", "status": "completed"}, + {"id": 85, "subject": "Task 16: Tree rendering + lazy load + selection in dialog", "status": "completed", "blockedBy": [83, 84]}, + {"id": 86, "subject": "Task 17: Error banner mapping + polish on dialog", "status": "completed", "blockedBy": [85]}, + {"id": 87, "subject": "Task 18: Add Override column + Browse button to InstanceConfigure.razor", "status": "completed", "blockedBy": [83, 86]}, + {"id": 88, "subject": "Task 19: End-to-end manual smoke (online + offline)", "status": "pending", "blockedBy": [70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87], "notes": "needs live rig — tracked in the 2026-08-01 pending-work task list"}, + {"id": 89, "subject": "Task 20: Update Component-DataConnectionLayer.md", "status": "completed", "blockedBy": [81]}, + {"id": 90, "subject": "Task 21: Update Component-TemplateEngine.md", "status": "completed", "blockedBy": [81]}, + {"id": 91, "subject": "Task 22: Update Component-CentralUI.md", "status": "completed", "blockedBy": [87]} ], - "lastUpdated": "2026-05-28T00:00:00Z" + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", + "lastUpdated": "2026-08-01" } diff --git a/docs/plans/2026-05-28-scadabridge-rename-design.md b/docs/plans/2026-05-28-scadabridge-rename-design.md index 5770c7c5..7dcb5d3a 100644 --- a/docs/plans/2026-05-28-scadabridge-rename-design.md +++ b/docs/plans/2026-05-28-scadabridge-rename-design.md @@ -1,7 +1,7 @@ # ScadaLink → ZB.MOM.WW.ScadaBridge Rename — Design **Date:** 2026-05-28 -**Status:** Approved, in implementation +**Status:** Implemented (commit `7b0b9c73`) **Scope:** Repo-wide rename of the product from "ScadaLink" to "ScadaBridge" and addition of the `ZB.MOM.WW` company prefix to every .NET project. Code, runtime artifacts (containers, network, databases), docs, and CLI config. ## Decisions @@ -115,7 +115,7 @@ Drop volume + re-seed. `docker compose -f infra/docker-compose.yml down -v` then ## Out of Scope - Sister repos `~/Desktop/MxAccessGateway`, `~/Desktop/OtOpcUa` — independent codebases, independent timelines. -- Repo folder `~/Desktop/scadalink-design` — left as-is to preserve Claude Code memory paths and user shell context. +- Repo folder `~/Desktop/scadalink-design` — left as-is to preserve Claude Code memory paths and user shell context. *(2026-08-01: this exclusion was later reversed and completed — see `2026-05-31-folder-repo-rename-scadabridge-plan.md` / `-design.md`, which renamed the folder and the Gitea repo to ScadaBridge.)* - Existing Transport bundle manifests with `SourceEnvironment = "docker-cluster"` — those values are deployment IDs and stay stable. If the user later wants to rename the deployment IDs themselves, that's a separate design. ## Verification diff --git a/docs/plans/2026-06-03-component-reference-docs.md.tasks.json b/docs/plans/2026-06-03-component-reference-docs.md.tasks.json index c0addcae..8070ddbe 100644 --- a/docs/plans/2026-06-03-component-reference-docs.md.tasks.json +++ b/docs/plans/2026-06-03-component-reference-docs.md.tasks.json @@ -1,34 +1,35 @@ { "planPath": "docs/plans/2026-06-03-component-reference-docs.md", "tasks": [ - {"id": 19, "subject": "Task 0: Scaffold + shared assets", "status": "pending"}, - {"id": 20, "subject": "Task 1: Pilot exemplar AuditLog.md (approval gate)", "status": "pending", "blockedBy": [19]}, - {"id": 21, "subject": "Task 2: Commons.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 22, "subject": "Task 3: ConfigurationDatabase.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 23, "subject": "Task 4: Communication.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 24, "subject": "Task 5: ClusterInfrastructure.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 25, "subject": "Task 6: Host.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 26, "subject": "Task 7: Security.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 27, "subject": "Task 8: TemplateEngine.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 28, "subject": "Task 9: DeploymentManager.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 29, "subject": "Task 10: SiteRuntime.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 30, "subject": "Task 11: DataConnectionLayer.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 31, "subject": "Task 12: StoreAndForward.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 32, "subject": "Task 13: ExternalSystemGateway.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 33, "subject": "Task 14: NotificationService.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 34, "subject": "Task 15: NotificationOutbox.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 35, "subject": "Task 16: SiteCallAudit.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 36, "subject": "Task 17: HealthMonitoring.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 37, "subject": "Task 18: SiteEventLogging.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 38, "subject": "Task 19: InboundAPI.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 39, "subject": "Task 20: ManagementService.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 40, "subject": "Task 21: CLI.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 41, "subject": "Task 22: Transport.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 42, "subject": "Task 23: CentralUI.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 43, "subject": "Task 24: TraefikProxy.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 44, "subject": "Task 25: TreeView.md reference doc", "status": "pending", "blockedBy": [20]}, - {"id": 45, "subject": "Task 26: Index + README link", "status": "pending", "blockedBy": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]}, - {"id": 46, "subject": "Task 27: Verification & fix pass", "status": "pending", "blockedBy": [45]} + {"id": 19, "subject": "Task 0: Scaffold + shared assets", "status": "completed"}, + {"id": 20, "subject": "Task 1: Pilot exemplar AuditLog.md (approval gate)", "status": "completed", "blockedBy": [19]}, + {"id": 21, "subject": "Task 2: Commons.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 22, "subject": "Task 3: ConfigurationDatabase.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 23, "subject": "Task 4: Communication.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 24, "subject": "Task 5: ClusterInfrastructure.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 25, "subject": "Task 6: Host.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 26, "subject": "Task 7: Security.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 27, "subject": "Task 8: TemplateEngine.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 28, "subject": "Task 9: DeploymentManager.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 29, "subject": "Task 10: SiteRuntime.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 30, "subject": "Task 11: DataConnectionLayer.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 31, "subject": "Task 12: StoreAndForward.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 32, "subject": "Task 13: ExternalSystemGateway.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 33, "subject": "Task 14: NotificationService.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 34, "subject": "Task 15: NotificationOutbox.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 35, "subject": "Task 16: SiteCallAudit.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 36, "subject": "Task 17: HealthMonitoring.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 37, "subject": "Task 18: SiteEventLogging.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 38, "subject": "Task 19: InboundAPI.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 39, "subject": "Task 20: ManagementService.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 40, "subject": "Task 21: CLI.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 41, "subject": "Task 22: Transport.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 42, "subject": "Task 23: CentralUI.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 43, "subject": "Task 24: TraefikProxy.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 44, "subject": "Task 25: TreeView.md reference doc", "status": "completed", "blockedBy": [20]}, + {"id": 45, "subject": "Task 26: Index + README link", "status": "completed", "blockedBy": [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]}, + {"id": 46, "subject": "Task 27: Verification & fix pass", "status": "completed", "blockedBy": [45]} ], - "lastUpdated": "2026-06-03" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-05-playwright-coverage-expansion.md.tasks.json b/docs/plans/2026-06-05-playwright-coverage-expansion.md.tasks.json index 2ed4fed1..37865537 100644 --- a/docs/plans/2026-06-05-playwright-coverage-expansion.md.tasks.json +++ b/docs/plans/2026-06-05-playwright-coverage-expansion.md.tasks.json @@ -1,23 +1,24 @@ { "planPath": "docs/plans/2026-06-05-playwright-coverage-expansion.md", - "lastUpdated": "2026-06-05T00:00:00Z", + "lastUpdated": "2026-08-01T00:00:00Z", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code", "nativeTaskIdBase": 57, "tasks": [ - {"id": 0, "nativeId": 57, "subject": "Task 0: Add CLI ProjectReference to test project", "status": "pending"}, - {"id": 1, "nativeId": 58, "subject": "Task 1: CliRunner core + ClusterAvailability probe", "status": "pending", "blockedBy": [0]}, - {"id": 2, "nativeId": 59, "subject": "Task 2: CliRunner typed fixture helpers", "status": "pending", "blockedBy": [1]}, - {"id": 3, "nativeId": 60, "subject": "Task 3: Standardize skip policy + skip-count logging", "status": "pending", "blockedBy": [1]}, - {"id": 4, "nativeId": 61, "subject": "Task 4: DeploymentFixture (ephemeral instance on site-a)", "status": "pending", "blockedBy": [2]}, - {"id": 5, "nativeId": 62, "subject": "Task 5: DeploymentActionTests.Deploy", "status": "pending", "blockedBy": [4]}, - {"id": 6, "nativeId": 63, "subject": "Task 6: DeploymentActionTests.Enable + Disable", "status": "pending", "blockedBy": [5]}, - {"id": 7, "nativeId": 64, "subject": "Task 7: DeploymentActionTests.Delete", "status": "pending", "blockedBy": [6]}, - {"id": 8, "nativeId": 65, "subject": "Task 8: Notification retry/discard + ParkedMessages query", "status": "pending", "blockedBy": [2]}, - {"id": 9, "nativeId": 66, "subject": "Task 9: Transport Import round-trip", "status": "pending", "blockedBy": [2]}, - {"id": 10, "nativeId": 67, "subject": "Task 10: Site CRUD round-trip", "status": "pending", "blockedBy": [2]}, - {"id": 11, "nativeId": 68, "subject": "Task 11: Template CRUD round-trip", "status": "pending", "blockedBy": [2]}, - {"id": 12, "nativeId": 69, "subject": "Task 12: LDAP mapping CRUD round-trip", "status": "pending", "blockedBy": [1]}, - {"id": 13, "nativeId": 70, "subject": "Task 13: Navigation render-assertion hardening", "status": "pending"}, - {"id": 14, "nativeId": 71, "subject": "Task 14: Health KPI load test", "status": "pending", "blockedBy": [1]}, - {"id": 15, "nativeId": 72, "subject": "Task 15: Full-suite verification + no-residue check", "status": "pending", "blockedBy": [3, 7, 8, 9, 10, 11, 12, 13, 14]} + {"id": 0, "nativeId": 57, "subject": "Task 0: Add CLI ProjectReference to test project", "status": "completed"}, + {"id": 1, "nativeId": 58, "subject": "Task 1: CliRunner core + ClusterAvailability probe", "status": "completed", "blockedBy": [0]}, + {"id": 2, "nativeId": 59, "subject": "Task 2: CliRunner typed fixture helpers", "status": "completed", "blockedBy": [1]}, + {"id": 3, "nativeId": 60, "subject": "Task 3: Standardize skip policy + skip-count logging", "status": "completed", "blockedBy": [1]}, + {"id": 4, "nativeId": 61, "subject": "Task 4: DeploymentFixture (ephemeral instance on site-a)", "status": "completed", "blockedBy": [2]}, + {"id": 5, "nativeId": 62, "subject": "Task 5: DeploymentActionTests.Deploy", "status": "completed", "blockedBy": [4]}, + {"id": 6, "nativeId": 63, "subject": "Task 6: DeploymentActionTests.Enable + Disable", "status": "completed", "blockedBy": [5]}, + {"id": 7, "nativeId": 64, "subject": "Task 7: DeploymentActionTests.Delete", "status": "completed", "blockedBy": [6]}, + {"id": 8, "nativeId": 65, "subject": "Task 8: Notification retry/discard + ParkedMessages query", "status": "completed", "blockedBy": [2]}, + {"id": 9, "nativeId": 66, "subject": "Task 9: Transport Import round-trip", "status": "completed", "blockedBy": [2]}, + {"id": 10, "nativeId": 67, "subject": "Task 10: Site CRUD round-trip", "status": "completed", "blockedBy": [2]}, + {"id": 11, "nativeId": 68, "subject": "Task 11: Template CRUD round-trip", "status": "completed", "blockedBy": [2]}, + {"id": 12, "nativeId": 69, "subject": "Task 12: LDAP mapping CRUD round-trip", "status": "completed", "blockedBy": [1]}, + {"id": 13, "nativeId": 70, "subject": "Task 13: Navigation render-assertion hardening", "status": "completed"}, + {"id": 14, "nativeId": 71, "subject": "Task 14: Health KPI load test", "status": "completed", "blockedBy": [1]}, + {"id": 15, "nativeId": 72, "subject": "Task 15: Full-suite verification + no-residue check", "status": "completed", "blockedBy": [3, 7, 8, 9, 10, 11, 12, 13, 14]} ] } diff --git a/docs/plans/2026-06-15-stillpending-completion-design.md b/docs/plans/2026-06-15-stillpending-completion-design.md index 7f661dbf..11293f34 100644 --- a/docs/plans/2026-06-15-stillpending-completion-design.md +++ b/docs/plans/2026-06-15-stillpending-completion-design.md @@ -78,7 +78,10 @@ Wire up behavior that exists in code but is never started, and fill the event-lo #### M5 — Audit hardening (T1–T8) Hash-chain tamper evidence (off by default, `verify-chain` made real); Parquet export/archival (replace the 501); per-channel retention overrides; tag-cascade for `ParentExecutionId` (thread writing-execution id through trigger-driven runs); ExecutionId/ParentExecutionId + SourceNode backfill on historical rows; per-node stuck-count KPIs; structured response capture (headers/content-type, inbound request headers, per-method opt-out, `AuditInboundCeilingHits` metric); CLI `audit tree`. -#### M6 — KPI History & Trends (T11 delivered; T9/T10 deferred) +#### M6 — KPI History & Trends (T11 delivered; T9/T10 **since DELIVERED 2026-06-19**) + +> **2026-08-01 bookkeeping sync:** the T9/T10 "deferred to the next major version" text below is superseded — **T9/T10 were delivered 2026-06-19 as the SMS (Twilio) adapter** (`SmsNotificationDeliveryAdapter`, `NotificationType.Sms`, Central UI Type selector + SMS recipient input; see `docs/plans/2026-06-19-sms-notifications.md`). **Teams was evaluated and dropped by design decision** — outbound-only backends cannot send 1:1 chat bodies via Graph without a Bot Framework inbound endpoint; SMS is inherently per-person and outbound-only. + Reshaped during the 2026-06-17 brainstorm (see `docs/plans/2026-06-17-m6-kpi-history-design.md`): - **T11 — DELIVERED** as the reusable **KPI-history backbone** (#26 KpiHistory), promoted from a notifications-only feature. A tall/EAV `KpiSample` store in **central MS SQL** (no new infra — supersedes the original "point-in-time only, no time-series store" stance), a `KpiHistoryRecorderActor` cluster singleton (`kpi-history-recorder`, not readiness-gated, best-effort with per-source isolation) sampling DI-registered `IKpiSampleSource`s every minute, a bucketed `GetRawSeriesAsync` + `KpiSeriesBucketer` query + scoped `KpiHistoryQueryService`, and a reusable custom-SVG `KpiTrendChart` (no third-party charting lib). Trends shipped for **all** current KPI sources — Notification Outbox, Site Call Audit, Audit Log, and Site Health — across four UI surfaces. - **T9 (Teams + other non-Email delivery adapters behind `INotificationDeliveryAdapter`) — DEFERRED to the next major version.** The seam exists; no code now. Transport choice (Incoming Webhook vs Microsoft Graph) and the Teams list-targeting model remain to be designed. @@ -132,7 +135,7 @@ This is the **final milestone** of the system-completion roadmap. All in-scope M ## Dependencies & sequencing - **M1 → M5** — audit hardening builds on the wired purge/reconciliation. -- **M6/T11** — delivered as the #26 KpiHistory backbone; reused **central MS SQL** (a tall/EAV `KpiSample` table) rather than introducing new infra. T9/T10 deferred to the next major version. +- **M6/T11** — delivered as the #26 KpiHistory backbone; reused **central MS SQL** (a tall/EAV `KpiSample` table) rather than introducing new infra. T9/T10 since delivered 2026-06-19 as the SMS (Twilio) adapter (see note above). - **M9/T26** — base-template versioning is the largest authoring item; may split. - **M4** — runs anytime; cheap and high-clarity, good to interleave. - **M3** — independent; can run in parallel with M1/M2. diff --git a/docs/plans/2026-06-15-stillpending-m2-implementation.md.tasks.json b/docs/plans/2026-06-15-stillpending-m2-implementation.md.tasks.json index c77e6c76..2c8360c8 100644 --- a/docs/plans/2026-06-15-stillpending-m2-implementation.md.tasks.json +++ b/docs/plans/2026-06-15-stillpending-m2-implementation.md.tasks.json @@ -27,9 +27,10 @@ {"ref": "#19", "subject": "script started/completed events", "status": "done in M1.8"} ], "followups": [ - {"id": 52, "subject": "Investigate 2 partition-purge E2E test failures (AuditLogPurgeActor/PartitionPurge)", "from": "M2.0", "status": "pending"}, - {"id": 53, "subject": "Dedup alarm-capable protocol predicate (3 copies → AlarmCapableProtocols)", "from": "M2.1", "status": "pending"}, - {"id": 54, "subject": "Expose ExecutionTimeoutSeconds (+ MinTimeBetweenRuns) in CLI + UI script authoring", "from": "M2.5", "status": "pending"} + {"id": 52, "subject": "Investigate 2 partition-purge E2E test failures (AuditLogPurgeActor/PartitionPurge)", "from": "M2.0", "status": "resolved", "note": "RESOLVED — commit 639e331d de-dated the 2 EndToEnd purge tests (M5.7)"}, + {"id": 53, "subject": "Dedup alarm-capable protocol predicate (3 copies → AlarmCapableProtocols)", "from": "M2.1", "status": "resolved", "note": "RESOLVED — shared Commons/Interfaces/Protocol/AlarmCapableProtocols.cs is now the single predicate"}, + {"id": 54, "subject": "Expose ExecutionTimeoutSeconds (+ MinTimeBetweenRuns) in CLI + UI script authoring", "from": "M2.5", "status": "resolved", "note": "RESOLVED — CLI --execution-timeout-seconds on template script add/update plus TemplateEdit binding"} ], - "lastUpdated": "2026-06-15" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-15-stillpending-phase1-implementation.md.tasks.json b/docs/plans/2026-06-15-stillpending-phase1-implementation.md.tasks.json index 1283e8a5..1ec833de 100644 --- a/docs/plans/2026-06-15-stillpending-phase1-implementation.md.tasks.json +++ b/docs/plans/2026-06-15-stillpending-phase1-implementation.md.tasks.json @@ -1,19 +1,20 @@ { "planPath": "docs/plans/2026-06-15-stillpending-phase1-implementation.md", "tasks": [ - {"id": 22, "subject": "M1.0: Confirm proto/site surface for audit pull (spike)", "status": "pending"}, - {"id": 23, "subject": "M1.1: Production gRPC IPullAuditEventsClient", "status": "pending", "blockedBy": [22]}, - {"id": 24, "subject": "M1.2: Wire reconciliation + purge actors as central singletons", "status": "pending"}, - {"id": 25, "subject": "M1.3: SiteCallAudit periodic reconciliation pull", "status": "pending"}, - {"id": 26, "subject": "M1.4: SiteCallAudit daily terminal-row purge scheduler", "status": "pending"}, - {"id": 27, "subject": "M1.5: SiteEventLog — emit Alarm events", "status": "pending"}, - {"id": 28, "subject": "M1.6: SiteEventLog — Deployment + Instance-lifecycle events", "status": "pending"}, - {"id": 29, "subject": "M1.7: SiteEventLog — Store-and-Forward + Notification events", "status": "pending"}, - {"id": 30, "subject": "M1.8: SiteEventLog — script started/completed (Info)", "status": "pending"}, - {"id": 31, "subject": "M1.9: M1 integration verification + redeploy", "status": "pending", "blockedBy": [23, 24, 25, 26, 27, 28, 29, 30]}, - {"id": 13, "subject": "M2 — Correctness & behavioral gaps (Tier 2) [umbrella; split per-item at execution]", "status": "pending"}, - {"id": 14, "subject": "M3 — Script trust boundary (Tier 1 #1-#2) [umbrella]", "status": "pending"}, - {"id": 15, "subject": "M4 — Doc reconciliation (Tier 4) [umbrella]", "status": "pending"} + {"id": 22, "subject": "M1.0: Confirm proto/site surface for audit pull (spike)", "status": "completed"}, + {"id": 23, "subject": "M1.1: Production gRPC IPullAuditEventsClient", "status": "completed", "blockedBy": [22]}, + {"id": 24, "subject": "M1.2: Wire reconciliation + purge actors as central singletons", "status": "completed"}, + {"id": 25, "subject": "M1.3: SiteCallAudit periodic reconciliation pull", "status": "completed"}, + {"id": 26, "subject": "M1.4: SiteCallAudit daily terminal-row purge scheduler", "status": "completed"}, + {"id": 27, "subject": "M1.5: SiteEventLog — emit Alarm events", "status": "completed"}, + {"id": 28, "subject": "M1.6: SiteEventLog — Deployment + Instance-lifecycle events", "status": "completed"}, + {"id": 29, "subject": "M1.7: SiteEventLog — Store-and-Forward + Notification events", "status": "completed"}, + {"id": 30, "subject": "M1.8: SiteEventLog — script started/completed (Info)", "status": "completed"}, + {"id": 31, "subject": "M1.9: M1 integration verification + redeploy", "status": "completed", "blockedBy": [23, 24, 25, 26, 27, 28, 29, 30]}, + {"id": 13, "subject": "M2 — Correctness & behavioral gaps (Tier 2) [umbrella; split per-item at execution]", "status": "completed"}, + {"id": 14, "subject": "M3 — Script trust boundary (Tier 1 #1-#2) [umbrella]", "status": "completed"}, + {"id": 15, "subject": "M4 — Doc reconciliation (Tier 4) [umbrella]", "status": "completed"} ], - "lastUpdated": "2026-06-15" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-16-ipsen-mes-movein.md.tasks.json b/docs/plans/2026-06-16-ipsen-mes-movein.md.tasks.json index 029728b5..5e8bbdb5 100644 --- a/docs/plans/2026-06-16-ipsen-mes-movein.md.tasks.json +++ b/docs/plans/2026-06-16-ipsen-mes-movein.md.tasks.json @@ -1,14 +1,15 @@ { "planPath": "docs/plans/2026-06-16-ipsen-mes-movein.md", "tasks": [ - {"id": 1, "subject": "Task 1: InboundDatabaseHelper (read-only DB for inbound scripts)", "classification": "standard", "status": "pending"}, - {"id": 2, "subject": "Task 2: Expose Database to inbound scripts (context + executor)", "classification": "high-risk", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 3: Build + regression-test ScadaBridge", "classification": "small", "status": "pending", "blockedBy": [2]}, - {"id": 4, "subject": "Task 4: Commit Component A", "classification": "trivial", "status": "pending", "blockedBy": [3]}, - {"id": 5, "subject": "Task 5: Deploy ScadaBridge Central (Component A) to wonder-app-vd03", "classification": "high-risk", "status": "pending", "blockedBy": [4]}, - {"id": 6, "subject": "Task 6: Apply Component B - update inbound IpsenMESMoveIn script", "classification": "high-risk", "status": "pending", "blockedBy": [5]}, - {"id": 7, "subject": "Task 7: Apply Component C - IpsenMoveIn template script on T1", "classification": "high-risk", "status": "pending", "blockedBy": [5]}, - {"id": 8, "subject": "Task 8: On-box end-to-end verification", "classification": "standard", "status": "pending", "blockedBy": [6, 7]} + {"id": 1, "subject": "Task 1: InboundDatabaseHelper (read-only DB for inbound scripts)", "classification": "standard", "status": "completed"}, + {"id": 2, "subject": "Task 2: Expose Database to inbound scripts (context + executor)", "classification": "high-risk", "status": "completed", "blockedBy": [1]}, + {"id": 3, "subject": "Task 3: Build + regression-test ScadaBridge", "classification": "small", "status": "completed", "blockedBy": [2]}, + {"id": 4, "subject": "Task 4: Commit Component A", "classification": "trivial", "status": "completed", "blockedBy": [3]}, + {"id": 5, "subject": "Task 5: Deploy ScadaBridge Central (Component A) to wonder-app-vd03", "classification": "high-risk", "status": "completed", "note": "done off-repo on wonder-app-vd03 (live methods authored against Database.* fixed there 2026-06-25)", "blockedBy": [4]}, + {"id": 6, "subject": "Task 6: Apply Component B - update inbound IpsenMESMoveIn script", "classification": "high-risk", "status": "completed", "note": "done off-repo on wonder-app-vd03 (live methods authored against Database.* fixed there 2026-06-25)", "blockedBy": [5]}, + {"id": 7, "subject": "Task 7: Apply Component C - IpsenMoveIn template script on T1", "classification": "high-risk", "status": "pending", "note": "NEEDS LIVE vd03 verification — tracked in the 2026-08-01 pending-work task list", "blockedBy": [5]}, + {"id": 8, "subject": "Task 8: On-box end-to-end verification", "classification": "standard", "status": "pending", "note": "NEEDS LIVE vd03 verification — tracked in the 2026-08-01 pending-work task list", "blockedBy": [6, 7]} ], - "lastUpdated": "2026-06-16" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-16-m5-audit-hardening.md.tasks.json b/docs/plans/2026-06-16-m5-audit-hardening.md.tasks.json index f3f9a514..082cfc3f 100644 --- a/docs/plans/2026-06-16-m5-audit-hardening.md.tasks.json +++ b/docs/plans/2026-06-16-m5-audit-hardening.md.tasks.json @@ -1,13 +1,14 @@ { "planPath": "docs/plans/2026-06-16-m5-audit-hardening.md", "tasks": [ - {"id": 119, "subject": "M5.1 (T8): CLI audit tree + tree endpoint", "status": "pending"}, - {"id": 120, "subject": "M5.2 (T6): Per-node stuck-count KPIs", "status": "pending"}, - {"id": 121, "subject": "M5.3 (T7): Structured response-capture increments", "status": "pending"}, - {"id": 122, "subject": "M5.4 (T4): ParentExecutionId tag-cascade", "status": "pending"}, - {"id": 123, "subject": "M5.5 (T3): Per-channel retention overrides", "status": "pending"}, - {"id": 124, "subject": "M5.6 (T5): SourceNode sentinel backfill + runbook", "status": "pending", "blockedBy": [119]}, - {"id": 125, "subject": "M5.7: M5 integration verification + docs", "status": "pending", "blockedBy": [119, 120, 121, 122, 123, 124]} + {"id": 119, "subject": "M5.1 (T8): CLI audit tree + tree endpoint", "status": "completed"}, + {"id": 120, "subject": "M5.2 (T6): Per-node stuck-count KPIs", "status": "completed"}, + {"id": 121, "subject": "M5.3 (T7): Structured response-capture increments", "status": "completed"}, + {"id": 122, "subject": "M5.4 (T4): ParentExecutionId tag-cascade", "status": "completed"}, + {"id": 123, "subject": "M5.5 (T3): Per-channel retention overrides", "status": "completed"}, + {"id": 124, "subject": "M5.6 (T5): SourceNode sentinel backfill + runbook", "status": "completed", "blockedBy": [119]}, + {"id": 125, "subject": "M5.7: M5 integration verification + docs", "status": "completed", "blockedBy": [119, 120, 121, 122, 123, 124]} ], - "lastUpdated": "2026-06-16" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-16-script-analysis-consolidation.md.tasks.json b/docs/plans/2026-06-16-script-analysis-consolidation.md.tasks.json index 294667ce..f3e441f8 100644 --- a/docs/plans/2026-06-16-script-analysis-consolidation.md.tasks.json +++ b/docs/plans/2026-06-16-script-analysis-consolidation.md.tasks.json @@ -1,13 +1,14 @@ { "planPath": "docs/plans/2026-06-16-script-analysis-consolidation.md", "tasks": [ - {"id": 103, "subject": "M3.0: Spike — compile-surface stub feasibility + package refs", "status": "pending"}, - {"id": 104, "subject": "M3.1: Build ScriptAnalysis project (policy+validator+compiler+surfaces+tests)", "status": "pending", "blockedBy": [103]}, - {"id": 105, "subject": "M3.2: TemplateEngine deploy gate → shared analyzer", "status": "pending", "blockedBy": [104]}, - {"id": 106, "subject": "M3.3: SiteRuntime → shared analyzer + parity test", "status": "pending", "blockedBy": [104]}, - {"id": 107, "subject": "M3.4: InboundAPI → shared analyzer", "status": "pending", "blockedBy": [104]}, - {"id": 108, "subject": "M3.5: CentralUI → shared analyzer (keep markers + Test-Run host)", "status": "pending", "blockedBy": [104]}, - {"id": 109, "subject": "M3.6: Integration verification + docs + fixture cleanup", "status": "pending", "blockedBy": [105, 106, 107, 108]} + {"id": 103, "subject": "M3.0: Spike — compile-surface stub feasibility + package refs", "status": "completed"}, + {"id": 104, "subject": "M3.1: Build ScriptAnalysis project (policy+validator+compiler+surfaces+tests)", "status": "completed", "blockedBy": [103]}, + {"id": 105, "subject": "M3.2: TemplateEngine deploy gate → shared analyzer", "status": "completed", "blockedBy": [104]}, + {"id": 106, "subject": "M3.3: SiteRuntime → shared analyzer + parity test", "status": "completed", "blockedBy": [104]}, + {"id": 107, "subject": "M3.4: InboundAPI → shared analyzer", "status": "completed", "blockedBy": [104]}, + {"id": 108, "subject": "M3.5: CentralUI → shared analyzer (keep markers + Test-Run host)", "status": "completed", "blockedBy": [104]}, + {"id": 109, "subject": "M3.6: Integration verification + docs + fixture cleanup", "status": "completed", "blockedBy": [105, 106, 107, 108]} ], - "lastUpdated": "2026-06-16" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-17-debugview-tabs-trees.md.tasks.json b/docs/plans/2026-06-17-debugview-tabs-trees.md.tasks.json index 3d58ea3c..cb9eafba 100644 --- a/docs/plans/2026-06-17-debugview-tabs-trees.md.tasks.json +++ b/docs/plans/2026-06-17-debugview-tabs-trees.md.tasks.json @@ -1,13 +1,13 @@ { "planPath": "docs/plans/2026-06-17-debugview-tabs-trees.md", "tasks": [ - {"id": 170, "subject": "DV-1: Native-binding linkage — additive AlarmStateChanged contract chain", "classification": "high-risk", "status": "pending"}, - {"id": 171, "subject": "DV-2: Site snapshot — placeholder rows for idle native source bindings", "classification": "standard", "status": "pending", "blockedBy": [170]}, - {"id": 172, "subject": "DV-3: DebugTreeNode model + attribute-tree builder", "classification": "standard", "status": "pending", "blockedBy": [170]}, - {"id": 173, "subject": "DV-4: Alarm-tree builder — computed leaves + native binding grouping + roll-up", "classification": "standard", "status": "pending", "blockedBy": [172, 170]}, - {"id": 174, "subject": "DV-5: DebugView page — tabs + two TreeViews + in-place updates", "classification": "standard", "status": "pending", "blockedBy": [173]}, - {"id": 175, "subject": "DV-6: Documentation — CentralUI + SiteRuntime + streaming contract", "classification": "small", "status": "pending", "blockedBy": [173]}, - {"id": 176, "subject": "DV-7: Integration — full build, docker rebuild, Playwright, smoke", "classification": "high-risk", "status": "pending", "blockedBy": [174, 175, 171]} + {"id": 170, "subject": "DV-1: Native-binding linkage — additive AlarmStateChanged contract chain", "classification": "high-risk", "status": "completed"}, + {"id": 171, "subject": "DV-2: Site snapshot — placeholder rows for idle native source bindings", "classification": "standard", "status": "completed", "blockedBy": [170]}, + {"id": 172, "subject": "DV-3: DebugTreeNode model + attribute-tree builder", "classification": "standard", "status": "completed", "blockedBy": [170]}, + {"id": 173, "subject": "DV-4: Alarm-tree builder — computed leaves + native binding grouping + roll-up", "classification": "standard", "status": "completed", "blockedBy": [172, 170]}, + {"id": 174, "subject": "DV-5: DebugView page — tabs + two TreeViews + in-place updates", "classification": "standard", "status": "completed", "blockedBy": [173]}, + {"id": 175, "subject": "DV-6: Documentation — CentralUI + SiteRuntime + streaming contract", "classification": "small", "status": "completed", "blockedBy": [173]}, + {"id": 176, "subject": "DV-7: Integration — full build, docker rebuild, Playwright, smoke", "classification": "high-risk", "status": "completed", "blockedBy": [174, 175, 171]} ], "waves": [ {"wave": 1, "tasks": [170]}, @@ -16,5 +16,6 @@ {"wave": 4, "tasks": [174, 175]}, {"wave": 5, "tasks": [176]} ], - "lastUpdated": "2026-06-17" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-17-m6-kpi-history.md.tasks.json b/docs/plans/2026-06-17-m6-kpi-history.md.tasks.json index c4c2de58..f528f815 100644 --- a/docs/plans/2026-06-17-m6-kpi-history.md.tasks.json +++ b/docs/plans/2026-06-17-m6-kpi-history.md.tasks.json @@ -1,23 +1,24 @@ { "planPath": "docs/plans/2026-06-17-m6-kpi-history.md", "tasks": [ - {"id": 1, "subject": "K1: Foundation contracts (Commons)", "nativeId": 136, "classification": "high-risk", "status": "pending"}, - {"id": 2, "subject": "K2: Persistence — EF config + repository + migration", "nativeId": 137, "classification": "high-risk", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "K3: KpiHistory project scaffold + options", "nativeId": 138, "classification": "standard", "status": "pending", "blockedBy": [1]}, - {"id": 4, "subject": "K4: KpiHistoryRecorderActor", "nativeId": 139, "classification": "high-risk", "status": "pending", "blockedBy": [3]}, - {"id": 5, "subject": "K5: Host wiring + appsettings", "nativeId": 140, "classification": "high-risk", "status": "pending", "blockedBy": [4]}, - {"id": 6, "subject": "K6: NotificationOutboxKpiSampleSource", "nativeId": 141, "classification": "small", "status": "pending", "blockedBy": [1]}, - {"id": 7, "subject": "K7: SiteCallAuditKpiSampleSource", "nativeId": 142, "classification": "small", "status": "pending", "blockedBy": [1]}, - {"id": 8, "subject": "K8: AuditLogKpiSampleSource", "nativeId": 143, "classification": "small", "status": "pending", "blockedBy": [1]}, - {"id": 9, "subject": "K9: SiteHealthKpiSampleSource", "nativeId": 144, "classification": "standard", "status": "pending", "blockedBy": [1]}, - {"id": 10, "subject": "K10: KpiSeriesBucketer (pure helper)", "nativeId": 145, "classification": "small", "status": "pending", "blockedBy": [1]}, - {"id": 11, "subject": "K11: KpiHistoryQueryService (CentralUI)", "nativeId": 146, "classification": "standard", "status": "pending", "blockedBy": [2, 10]}, - {"id": 12, "subject": "K12: KpiTrendChart.razor reusable SVG component", "nativeId": 147, "classification": "standard", "status": "pending", "blockedBy": [1]}, - {"id": 13, "subject": "K13: Notification Outbox page trend section", "nativeId": 148, "classification": "standard", "status": "pending", "blockedBy": [11, 12]}, - {"id": 14, "subject": "K14: Site Calls page trend section", "nativeId": 149, "classification": "standard", "status": "pending", "blockedBy": [11, 12]}, - {"id": 15, "subject": "K15: Audit Log page trend section", "nativeId": 150, "classification": "standard", "status": "pending", "blockedBy": [11, 12]}, - {"id": 16, "subject": "K16: Health dashboard per-site trend panel", "nativeId": 151, "classification": "standard", "status": "pending", "blockedBy": [11, 12]}, - {"id": 17, "subject": "K17: Integration — docs, deploy, Playwright, full verification", "nativeId": 152, "classification": "high-risk", "status": "pending", "blockedBy": [5, 6, 7, 8, 9, 13, 14, 15, 16]} + {"id": 1, "subject": "K1: Foundation contracts (Commons)", "nativeId": 136, "classification": "high-risk", "status": "completed"}, + {"id": 2, "subject": "K2: Persistence — EF config + repository + migration", "nativeId": 137, "classification": "high-risk", "status": "completed", "blockedBy": [1]}, + {"id": 3, "subject": "K3: KpiHistory project scaffold + options", "nativeId": 138, "classification": "standard", "status": "completed", "blockedBy": [1]}, + {"id": 4, "subject": "K4: KpiHistoryRecorderActor", "nativeId": 139, "classification": "high-risk", "status": "completed", "blockedBy": [3]}, + {"id": 5, "subject": "K5: Host wiring + appsettings", "nativeId": 140, "classification": "high-risk", "status": "completed", "blockedBy": [4]}, + {"id": 6, "subject": "K6: NotificationOutboxKpiSampleSource", "nativeId": 141, "classification": "small", "status": "completed", "blockedBy": [1]}, + {"id": 7, "subject": "K7: SiteCallAuditKpiSampleSource", "nativeId": 142, "classification": "small", "status": "completed", "blockedBy": [1]}, + {"id": 8, "subject": "K8: AuditLogKpiSampleSource", "nativeId": 143, "classification": "small", "status": "completed", "blockedBy": [1]}, + {"id": 9, "subject": "K9: SiteHealthKpiSampleSource", "nativeId": 144, "classification": "standard", "status": "completed", "blockedBy": [1]}, + {"id": 10, "subject": "K10: KpiSeriesBucketer (pure helper)", "nativeId": 145, "classification": "small", "status": "completed", "blockedBy": [1]}, + {"id": 11, "subject": "K11: KpiHistoryQueryService (CentralUI)", "nativeId": 146, "classification": "standard", "status": "completed", "blockedBy": [2, 10]}, + {"id": 12, "subject": "K12: KpiTrendChart.razor reusable SVG component", "nativeId": 147, "classification": "standard", "status": "completed", "blockedBy": [1]}, + {"id": 13, "subject": "K13: Notification Outbox page trend section", "nativeId": 148, "classification": "standard", "status": "completed", "blockedBy": [11, 12]}, + {"id": 14, "subject": "K14: Site Calls page trend section", "nativeId": 149, "classification": "standard", "status": "completed", "blockedBy": [11, 12]}, + {"id": 15, "subject": "K15: Audit Log page trend section", "nativeId": 150, "classification": "standard", "status": "completed", "blockedBy": [11, 12]}, + {"id": 16, "subject": "K16: Health dashboard per-site trend panel", "nativeId": 151, "classification": "standard", "status": "completed", "blockedBy": [11, 12]}, + {"id": 17, "subject": "K17: Integration — docs, deploy, Playwright, full verification", "nativeId": 152, "classification": "high-risk", "status": "completed", "blockedBy": [5, 6, 7, 8, 9, 13, 14, 15, 16]} ], - "lastUpdated": "2026-06-17" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-17-waitasync-compile-surface-mirror-gaps.md.tasks.json b/docs/plans/2026-06-17-waitasync-compile-surface-mirror-gaps.md.tasks.json index 686c3535..5b0ccf89 100644 --- a/docs/plans/2026-06-17-waitasync-compile-surface-mirror-gaps.md.tasks.json +++ b/docs/plans/2026-06-17-waitasync-compile-surface-mirror-gaps.md.tasks.json @@ -11,7 +11,8 @@ ], "reviewPolishCommit": "8dcc55f", "followUps": [ - {"id": 162, "subject": "normalize routed GetAttributes List values for cross-process transport (surfaced by WS-4 review)"} + {"id": 162, "subject": "normalize routed GetAttributes List values for cross-process transport (surfaced by WS-4 review)", "status": "resolved", "note": "RESOLVED — DeploymentManagerActor.cs RouteInboundApiGetAttributes now normalizes via NormalizeRoutedReturnValue"} ], - "lastUpdated": "2026-06-17" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-17-waitfor-attribute-change-helper-spec.md b/docs/plans/2026-06-17-waitfor-attribute-change-helper-spec.md index 3ae62aac..8261e518 100644 --- a/docs/plans/2026-06-17-waitfor-attribute-change-helper-spec.md +++ b/docs/plans/2026-06-17-waitfor-attribute-change-helper-spec.md @@ -239,7 +239,9 @@ fully cover the DELMIA/MES use case. > **Status:** IMPLEMENTED. `Route.To(code).WaitForAttribute(name, targetValue, timeout)` is wired > end-to-end (`RouteToWaitForAttributeRequest/Response` → `IInstanceRouter` → `CommunicationService` > → `SiteCommunicationActor` → `DeploymentManagerActor` → `InstanceActor`), value-equality only -> across the wire. NOT wired into the CentralUI Test-Run sandbox — that remains a follow-up. +> across the wire. ~~NOT wired into the CentralUI Test-Run sandbox — that remains a follow-up.~~ +> **Follow-up SHIPPED 2026-07-10** — the CentralUI Test-Run sandbox is wired via +> `SandboxInstanceGateway`/`SandboxScriptHost` (deferred-work register row #14 resolved). --- diff --git a/docs/plans/2026-06-17-waitfor-deferred-items.md.tasks.json b/docs/plans/2026-06-17-waitfor-deferred-items.md.tasks.json index e3f85161..c5a02b3d 100644 --- a/docs/plans/2026-06-17-waitfor-deferred-items.md.tasks.json +++ b/docs/plans/2026-06-17-waitfor-deferred-items.md.tasks.json @@ -1,10 +1,11 @@ { "planPath": "docs/plans/2026-06-17-waitfor-deferred-items.md", "tasks": [ - {"id": 1, "subject": "WD-1: site-local WaitForAsync + WaitResult + quality-gated mode (§3+§4.2)", "classification": "high-risk", "status": "pending", "parallelizableWith": [2]}, - {"id": 2, "subject": "WD-2a: routed contract + central path (§6 part 1)", "classification": "high-risk", "status": "pending", "parallelizableWith": [1]}, - {"id": 3, "subject": "WD-2b: site unpacking + DeploymentManager handler (§6 part 2)", "classification": "high-risk", "status": "pending", "blockedBy": [2]}, - {"id": 4, "subject": "WD-3: integration — docs + full verification", "classification": "standard", "status": "pending", "blockedBy": [1, 2, 3]} + {"id": 1, "subject": "WD-1: site-local WaitForAsync + WaitResult + quality-gated mode (§3+§4.2)", "classification": "high-risk", "status": "completed", "parallelizableWith": [2]}, + {"id": 2, "subject": "WD-2a: routed contract + central path (§6 part 1)", "classification": "high-risk", "status": "completed", "parallelizableWith": [1]}, + {"id": 3, "subject": "WD-2b: site unpacking + DeploymentManager handler (§6 part 2)", "classification": "high-risk", "status": "completed", "blockedBy": [2]}, + {"id": 4, "subject": "WD-3: integration — docs + full verification", "classification": "standard", "status": "completed", "blockedBy": [1, 2, 3]} ], - "lastUpdated": "2026-06-17" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-18-m10-uiux-platform.md b/docs/plans/2026-06-18-m10-uiux-platform.md index 775e7d66..0ca117ac 100644 --- a/docs/plans/2026-06-18-m10-uiux-platform.md +++ b/docs/plans/2026-06-18-m10-uiux-platform.md @@ -464,7 +464,7 @@ Markup: two `` with `id="@(IdPrefix)-from"`/`-to`, - Theme-package side-rail dark theming, IF the spike verdict is "rail stays light" (coordination follow-up). ## Follow-ups logged at delivery (INT findings) -- **#207 (pre-existing, open since M6/K14):** `QueryStringDrillInTests` fixture does not register `IKpiHistoryQueryService` — 3 `SiteCallsReport` drill-in tests red on this gap; unrelated to M10. +- **#207 — FIXED (2026-08-01 bookkeeping sync).** ~~(pre-existing, open since M6/K14):~~ `QueryStringDrillInTests` now registers the `IKpiHistoryQueryService` substitute in its fixture (lines ~165-167), so the 3 `SiteCallsReport` drill-in tests are green. - **#163 (pre-existing):** `InstanceConfigureListOverrideTests` codec roundtrip red; pre-dates M10. - **NotificationReport `OffsetPager` always-visible:** pager is now always visible when results exist (previously hidden on sub-page-size sets); buttons are correctly disabled on a single page — product decision whether to re-add an `@if (_totalCount > _pageSize)` guard. - **`Deployments.razor` PagerWindow intentionally kept:** windowed numbered-button UX is deliberate; NOT migrated to `OffsetPager`. diff --git a/docs/plans/2026-06-18-m10-uiux-platform.md.tasks.json b/docs/plans/2026-06-18-m10-uiux-platform.md.tasks.json index 32c985ac..1752417d 100644 --- a/docs/plans/2026-06-18-m10-uiux-platform.md.tasks.json +++ b/docs/plans/2026-06-18-m10-uiux-platform.md.tasks.json @@ -1,23 +1,24 @@ { "planPath": "docs/plans/2026-06-18-m10-uiux-platform.md", "tasks": [ - {"id": 269, "subject": "M10-T33a: Modal host ShowAsync + focus trap/restore + backdrop hook", "status": "pending"}, - {"id": 270, "subject": "M10-T34-spike: Verify ZB.MOM.WW.Theme dark-mode feasibility", "status": "pending"}, - {"id": 271, "subject": "M10-T41: Alarm-override Playwright trigger-config scenarios", "status": "pending"}, - {"id": 272, "subject": "M10-T33b: Migrate 5 simple dialogs to ShowAsync", "status": "pending", "blockedBy": [269]}, - {"id": 273, "subject": "M10-T34a: Dark token layer in site.css + .sb-modal-backdrop", "status": "pending", "blockedBy": [270]}, - {"id": 274, "subject": "M10-T35a: OffsetPager component", "status": "pending"}, - {"id": 275, "subject": "M10-T35b: KeysetPager component", "status": "pending"}, - {"id": 276, "subject": "M10-T35c: DateTimeRangeFilter component", "status": "pending"}, - {"id": 277, "subject": "M10-T35d: Adopt OffsetPager+DateTimeRangeFilter into NotificationReport (+backdrop token)", "status": "pending", "blockedBy": [274, 276, 273]}, - {"id": 278, "subject": "M10-T35e: Adopt OffsetPager+DateTimeRangeFilter into ConfigurationAuditLog", "status": "pending", "blockedBy": [274, 276]}, - {"id": 279, "subject": "M10-T35f: Adopt KeysetPager+DateTimeRangeFilter into SiteCallsReport (+backdrop token)", "status": "pending", "blockedBy": [275, 276, 273]}, - {"id": 280, "subject": "M10-T35g: Adopt KeysetPager into AuditResultsGrid + DateTimeRangeFilter into AuditFilterBar", "status": "pending", "blockedBy": [275, 276]}, - {"id": 281, "subject": "M10-T35h: Adopt DateTimeRangeFilter into EventLogs", "status": "pending", "blockedBy": [276]}, - {"id": 282, "subject": "M10-T34c: Tokenize remaining standalone backdrops + bg-* audit", "status": "pending", "blockedBy": [273]}, - {"id": 283, "subject": "M10-T34b: Dark-mode toggle + persistence + SSR hydration", "status": "pending", "blockedBy": [270, 273]}, - {"id": 284, "subject": "M10-T36a: Accessibility pass — TreeView chevron + icon audit + toast verify", "status": "pending"}, - {"id": 285, "subject": "M10-INT: Integration — build, docker, Playwright, a11y+dark smoke, docs, review", "status": "pending", "blockedBy": [271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284]} + {"id": 269, "subject": "M10-T33a: Modal host ShowAsync + focus trap/restore + backdrop hook", "status": "completed"}, + {"id": 270, "subject": "M10-T34-spike: Verify ZB.MOM.WW.Theme dark-mode feasibility", "status": "completed"}, + {"id": 271, "subject": "M10-T41: Alarm-override Playwright trigger-config scenarios", "status": "completed"}, + {"id": 272, "subject": "M10-T33b: Migrate 5 simple dialogs to ShowAsync", "status": "completed", "blockedBy": [269]}, + {"id": 273, "subject": "M10-T34a: Dark token layer in site.css + .sb-modal-backdrop", "status": "completed", "blockedBy": [270]}, + {"id": 274, "subject": "M10-T35a: OffsetPager component", "status": "completed"}, + {"id": 275, "subject": "M10-T35b: KeysetPager component", "status": "completed"}, + {"id": 276, "subject": "M10-T35c: DateTimeRangeFilter component", "status": "completed"}, + {"id": 277, "subject": "M10-T35d: Adopt OffsetPager+DateTimeRangeFilter into NotificationReport (+backdrop token)", "status": "completed", "blockedBy": [274, 276, 273]}, + {"id": 278, "subject": "M10-T35e: Adopt OffsetPager+DateTimeRangeFilter into ConfigurationAuditLog", "status": "completed", "blockedBy": [274, 276]}, + {"id": 279, "subject": "M10-T35f: Adopt KeysetPager+DateTimeRangeFilter into SiteCallsReport (+backdrop token)", "status": "completed", "blockedBy": [275, 276, 273]}, + {"id": 280, "subject": "M10-T35g: Adopt KeysetPager into AuditResultsGrid + DateTimeRangeFilter into AuditFilterBar", "status": "completed", "blockedBy": [275, 276]}, + {"id": 281, "subject": "M10-T35h: Adopt DateTimeRangeFilter into EventLogs", "status": "completed", "blockedBy": [276]}, + {"id": 282, "subject": "M10-T34c: Tokenize remaining standalone backdrops + bg-* audit", "status": "completed", "blockedBy": [273]}, + {"id": 283, "subject": "M10-T34b: Dark-mode toggle + persistence + SSR hydration", "status": "completed", "blockedBy": [270, 273]}, + {"id": 284, "subject": "M10-T36a: Accessibility pass — TreeView chevron + icon audit + toast verify", "status": "completed"}, + {"id": 285, "subject": "M10-INT: Integration — build, docker, Playwright, a11y+dark smoke, docs, review", "status": "completed", "blockedBy": [271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284]} ], - "lastUpdated": "2026-06-18" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-18-m7-opcua-mxgateway-ux.md.tasks.json b/docs/plans/2026-06-18-m7-opcua-mxgateway-ux.md.tasks.json index 69538c0d..225ae33c 100644 --- a/docs/plans/2026-06-18-m7-opcua-mxgateway-ux.md.tasks.json +++ b/docs/plans/2026-06-18-m7-opcua-mxgateway-ux.md.tasks.json @@ -4,28 +4,28 @@ "branch": "worktree-m7-opcua-mxgateway-ux", "baseSha": "254e0e7", "tasks": [ - {"id": 184, "subject": "M7-A1: Extract AlarmStateBadges shared component", "classification": "standard", "status": "pending"}, - {"id": 185, "subject": "M7-A2: Operator Alarm Summary page + fan-out service", "classification": "standard", "status": "pending", "blockedBy": [184]}, - {"id": 186, "subject": "M7-A3: Operator + Verifier roles + policies + LDAP mapping", "classification": "high-risk", "status": "pending"}, - {"id": 187, "subject": "M7-B1: Browse type-info fields on BrowseNode", "classification": "standard", "status": "pending"}, - {"id": 188, "subject": "M7-B2: BrowseNext continuation through browse contract", "classification": "high-risk", "status": "pending", "blockedBy": [187]}, - {"id": 189, "subject": "M7-B3: Thread continuation token through browse plumbing", "classification": "standard", "status": "pending", "blockedBy": [188]}, - {"id": 190, "subject": "M7-B4: Bounded recursive address-space search — adapter", "classification": "high-risk", "status": "pending", "blockedBy": [188]}, - {"id": 191, "subject": "M7-B5: Search plumbing — message + actor + comm + service", "classification": "standard", "status": "pending", "blockedBy": [189, 190]}, - {"id": 192, "subject": "M7-B6: NodeBrowserDialog — load-more + search box + type column", "classification": "standard", "status": "pending", "blockedBy": [189, 191]}, - {"id": 193, "subject": "M7-B7: Verify-endpoint — message + site probe handler", "classification": "high-risk", "status": "pending", "blockedBy": [191]}, - {"id": 194, "subject": "M7-B8: Verify-endpoint plumbing + UI", "classification": "standard", "status": "pending", "blockedBy": [193]}, - {"id": 195, "subject": "M7-B9: Cert trust — per-node CertStore actor + broadcast", "classification": "high-risk", "status": "pending", "blockedBy": [193]}, - {"id": 196, "subject": "M7-B10: Cert trust plumbing + cert-management UI", "classification": "standard", "status": "pending", "blockedBy": [194, 195]}, - {"id": 197, "subject": "M7-C1: PendingSecuredWrite entity + persistence + migration", "classification": "high-risk", "status": "pending"}, - {"id": 198, "subject": "M7-C2: Secured-write commands + submit/reject/list handlers", "classification": "high-risk", "status": "pending", "blockedBy": [197, 186]}, - {"id": 199, "subject": "M7-C3: Approve → site write relay", "classification": "high-risk", "status": "pending", "blockedBy": [198]}, - {"id": 200, "subject": "M7-C4: AuditKind.SecuredWrite + audit wiring", "classification": "high-risk", "status": "pending", "blockedBy": [198, 199]}, - {"id": 201, "subject": "M7-C5: Secured Writes Central UI page", "classification": "standard", "status": "pending", "blockedBy": [198, 199, 186]}, - {"id": 202, "subject": "M7-D1: OverrideCsvParser pure helper", "classification": "standard", "status": "pending"}, - {"id": 203, "subject": "M7-D2: InstanceConfigure CSV import UI", "classification": "standard", "status": "pending", "blockedBy": [202]}, - {"id": 204, "subject": "M7-D3: CLI instance import-overrides --file", "classification": "small", "status": "pending", "blockedBy": [202]}, - {"id": 205, "subject": "M7-E1: Integration — docs, full build, docker rebuild, Playwright, smoke", "classification": "high-risk", "status": "pending", "blockedBy": [184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204]} + {"id": 184, "subject": "M7-A1: Extract AlarmStateBadges shared component", "classification": "standard", "status": "completed"}, + {"id": 185, "subject": "M7-A2: Operator Alarm Summary page + fan-out service", "classification": "standard", "status": "completed", "blockedBy": [184]}, + {"id": 186, "subject": "M7-A3: Operator + Verifier roles + policies + LDAP mapping", "classification": "high-risk", "status": "completed"}, + {"id": 187, "subject": "M7-B1: Browse type-info fields on BrowseNode", "classification": "standard", "status": "completed"}, + {"id": 188, "subject": "M7-B2: BrowseNext continuation through browse contract", "classification": "high-risk", "status": "completed", "blockedBy": [187]}, + {"id": 189, "subject": "M7-B3: Thread continuation token through browse plumbing", "classification": "standard", "status": "completed", "blockedBy": [188]}, + {"id": 190, "subject": "M7-B4: Bounded recursive address-space search — adapter", "classification": "high-risk", "status": "completed", "blockedBy": [188]}, + {"id": 191, "subject": "M7-B5: Search plumbing — message + actor + comm + service", "classification": "standard", "status": "completed", "blockedBy": [189, 190]}, + {"id": 192, "subject": "M7-B6: NodeBrowserDialog — load-more + search box + type column", "classification": "standard", "status": "completed", "blockedBy": [189, 191]}, + {"id": 193, "subject": "M7-B7: Verify-endpoint — message + site probe handler", "classification": "high-risk", "status": "completed", "blockedBy": [191]}, + {"id": 194, "subject": "M7-B8: Verify-endpoint plumbing + UI", "classification": "standard", "status": "completed", "blockedBy": [193]}, + {"id": 195, "subject": "M7-B9: Cert trust — per-node CertStore actor + broadcast", "classification": "high-risk", "status": "completed", "blockedBy": [193]}, + {"id": 196, "subject": "M7-B10: Cert trust plumbing + cert-management UI", "classification": "standard", "status": "completed", "blockedBy": [194, 195]}, + {"id": 197, "subject": "M7-C1: PendingSecuredWrite entity + persistence + migration", "classification": "high-risk", "status": "completed"}, + {"id": 198, "subject": "M7-C2: Secured-write commands + submit/reject/list handlers", "classification": "high-risk", "status": "completed", "blockedBy": [197, 186]}, + {"id": 199, "subject": "M7-C3: Approve → site write relay", "classification": "high-risk", "status": "completed", "blockedBy": [198]}, + {"id": 200, "subject": "M7-C4: AuditKind.SecuredWrite + audit wiring", "classification": "high-risk", "status": "completed", "blockedBy": [198, 199]}, + {"id": 201, "subject": "M7-C5: Secured Writes Central UI page", "classification": "standard", "status": "completed", "blockedBy": [198, 199, 186]}, + {"id": 202, "subject": "M7-D1: OverrideCsvParser pure helper", "classification": "standard", "status": "completed"}, + {"id": 203, "subject": "M7-D2: InstanceConfigure CSV import UI", "classification": "standard", "status": "completed", "blockedBy": [202]}, + {"id": 204, "subject": "M7-D3: CLI instance import-overrides --file", "classification": "small", "status": "completed", "blockedBy": [202]}, + {"id": 205, "subject": "M7-E1: Integration — docs, full build, docker rebuild, Playwright, smoke", "classification": "high-risk", "status": "completed", "blockedBy": [184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204]} ], "waves": { "A": [184, 185, 186], @@ -34,5 +34,6 @@ "D": [202, 203, 204], "E": [205] }, - "lastUpdated": "2026-06-18" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-18-m8-transport.md.tasks.json b/docs/plans/2026-06-18-m8-transport.md.tasks.json index 78dea29d..f3be63ae 100644 --- a/docs/plans/2026-06-18-m8-transport.md.tasks.json +++ b/docs/plans/2026-06-18-m8-transport.md.tasks.json @@ -1,21 +1,22 @@ { "planPath": "docs/plans/2026-06-18-m8-transport.md", "tasks": [ - {"id": 210, "subject": "M8-A1: Commons name-map types + preview/selection/summary extensions", "status": "pending"}, - {"id": 211, "subject": "M8-A2: Transport bundle DTOs — Site/DataConnection/Instance + BundleContentDto", "status": "pending"}, - {"id": 212, "subject": "M8-A3: LineDiffer — pure Myers per-line diff helper (T20)", "status": "pending"}, - {"id": 213, "subject": "M8-B1: DependencyResolver site/instance expansion", "status": "pending", "blockedBy": [211]}, - {"id": 214, "subject": "M8-B2: EntitySerializer site/connection/instance mapping (both directions)", "status": "pending", "blockedBy": [211]}, - {"id": 215, "subject": "M8-B3: ManifestBuilder + summary counts + schemaVersion 1.1", "status": "pending", "blockedBy": [210]}, - {"id": 216, "subject": "M8-B4: Export plumbing — selection wiring, command, ManagementActor, CLI", "status": "pending", "blockedBy": [213, 214, 215]}, - {"id": 217, "subject": "M8-C1: ArtifactDiff — Myers code diff (T20) + Site/Connection/Instance compares", "status": "pending", "blockedBy": [211, 212]}, - {"id": 218, "subject": "M8-C2: BundleImporter.PreviewAsync — new-type diff + required-mapping detection + blockers", "status": "pending", "blockedBy": [214, 217]}, - {"id": 219, "subject": "M8-D1: BundleImporter.ApplyAsync — nameMap, resolve-or-create, instance upsert + FK rewire", "status": "pending", "blockedBy": [218]}, - {"id": 220, "subject": "M8-D2: #16 — real stale-instance enumeration in ImportResult", "status": "pending", "blockedBy": [219]}, - {"id": 221, "subject": "M8-D3: Import plumbing — name-map through command, ManagementActor, CLI", "status": "pending", "blockedBy": [219, 216]}, - {"id": 222, "subject": "M8-E1: Export wizard — Sites/Instances selection", "status": "pending", "blockedBy": [216]}, - {"id": 223, "subject": "M8-E2: Import wizard — Map step + Modified +/- diff render", "status": "pending", "blockedBy": [221]}, - {"id": 224, "subject": "M8-INT: Docs, full build, docker rebuild, Playwright, live smoke, end-to-end trace", "status": "pending", "blockedBy": [222, 223]} + {"id": 210, "subject": "M8-A1: Commons name-map types + preview/selection/summary extensions", "status": "completed"}, + {"id": 211, "subject": "M8-A2: Transport bundle DTOs — Site/DataConnection/Instance + BundleContentDto", "status": "completed"}, + {"id": 212, "subject": "M8-A3: LineDiffer — pure Myers per-line diff helper (T20)", "status": "completed"}, + {"id": 213, "subject": "M8-B1: DependencyResolver site/instance expansion", "status": "completed", "blockedBy": [211]}, + {"id": 214, "subject": "M8-B2: EntitySerializer site/connection/instance mapping (both directions)", "status": "completed", "blockedBy": [211]}, + {"id": 215, "subject": "M8-B3: ManifestBuilder + summary counts + schemaVersion 1.1", "status": "completed", "blockedBy": [210]}, + {"id": 216, "subject": "M8-B4: Export plumbing — selection wiring, command, ManagementActor, CLI", "status": "completed", "blockedBy": [213, 214, 215]}, + {"id": 217, "subject": "M8-C1: ArtifactDiff — Myers code diff (T20) + Site/Connection/Instance compares", "status": "completed", "blockedBy": [211, 212]}, + {"id": 218, "subject": "M8-C2: BundleImporter.PreviewAsync — new-type diff + required-mapping detection + blockers", "status": "completed", "blockedBy": [214, 217]}, + {"id": 219, "subject": "M8-D1: BundleImporter.ApplyAsync — nameMap, resolve-or-create, instance upsert + FK rewire", "status": "completed", "blockedBy": [218]}, + {"id": 220, "subject": "M8-D2: #16 — real stale-instance enumeration in ImportResult", "status": "completed", "blockedBy": [219]}, + {"id": 221, "subject": "M8-D3: Import plumbing — name-map through command, ManagementActor, CLI", "status": "completed", "blockedBy": [219, 216]}, + {"id": 222, "subject": "M8-E1: Export wizard — Sites/Instances selection", "status": "completed", "blockedBy": [216]}, + {"id": 223, "subject": "M8-E2: Import wizard — Map step + Modified +/- diff render", "status": "completed", "blockedBy": [221]}, + {"id": 224, "subject": "M8-INT: Docs, full build, docker rebuild, Playwright, live smoke, end-to-end trace", "status": "completed", "blockedBy": [222, 223]} ], - "lastUpdated": "2026-06-18" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-18-m9-templates-authoring.md.tasks.json b/docs/plans/2026-06-18-m9-templates-authoring.md.tasks.json index cd597b2e..978ffb89 100644 --- a/docs/plans/2026-06-18-m9-templates-authoring.md.tasks.json +++ b/docs/plans/2026-06-18-m9-templates-authoring.md.tasks.json @@ -4,23 +4,23 @@ "branch": "worktree-m9-templates-authoring", "baseRef": "72aec3b4", "tasks": [ - {"id": 240, "label": "T22", "subject": "M9-T22: Template tree search box", "classification": "small", "wave": 1, "status": "pending"}, - {"id": 241, "label": "CLI", "subject": "M9-CLI: cached-call retry/discard CLI group", "classification": "small", "wave": 1, "status": "pending"}, - {"id": 242, "label": "T28a", "subject": "M9-T28a: Strict expression-trigger kind — backend", "classification": "small", "wave": 1, "status": "pending"}, - {"id": 243, "label": "T28b", "subject": "M9-T28b: Strict trigger-kind — UI selector + CLI flag", "classification": "small", "wave": 1, "status": "pending", "blockedBy": [242]}, - {"id": 244, "label": "T23a", "subject": "M9-T23a: Folder sibling reorder — service + command + handler", "classification": "standard", "wave": 2, "status": "pending"}, - {"id": 245, "label": "T23b", "subject": "M9-T23b: Folder reorder + root context menu — UI", "classification": "standard", "wave": 2, "status": "pending", "blockedBy": [240, 244]}, - {"id": 246, "label": "T25", "subject": "M9-T25: Connection live-status indicators", "classification": "standard", "wave": 2, "status": "pending"}, - {"id": 247, "label": "T24a", "subject": "M9-T24a: Move data connection between sites — command + handler + guards", "classification": "high-risk", "wave": 3, "status": "pending"}, - {"id": 248, "label": "T24b", "subject": "M9-T24b: Move connection — UI dialog + action", "classification": "standard", "wave": 3, "status": "pending", "blockedBy": [247, 246]}, - {"id": 249, "label": "T32a", "subject": "M9-T32a: SharedSchema entity + EF config + migration + repo", "classification": "high-risk", "wave": 3, "status": "pending"}, - {"id": 250, "label": "T32b", "subject": "M9-T32b: JSON Schema $ref resolver + deploy-time validation", "classification": "high-risk", "wave": 3, "status": "pending", "blockedBy": [249]}, - {"id": 251, "label": "T32c", "subject": "M9-T32c: Schema library — CRUD commands + handlers + Central UI page", "classification": "high-risk", "wave": 4, "status": "pending", "blockedBy": [249, 250]}, - {"id": 252, "label": "T30", "subject": "M9-T30: Schema-driven nested value-entry forms", "classification": "standard", "wave": 4, "status": "pending", "blockedBy": [250]}, - {"id": 253, "label": "T31", "subject": "M9-T31: Monaco JSON-Schema hover/completion", "classification": "standard", "wave": 4, "status": "pending", "blockedBy": [250, 252]}, - {"id": 254, "label": "T26a", "subject": "M9-T26a: Inheritance resolve service + query command", "classification": "high-risk", "wave": 4, "status": "pending"}, - {"id": 255, "label": "T26b", "subject": "M9-T26b: TemplateEdit — full inherited set + staleness banner", "classification": "standard", "wave": 4, "status": "pending", "blockedBy": [254]}, - {"id": 256, "label": "INT", "subject": "M9-INT: Integration — build, docker, Playwright, smoke, end-to-end trace", "classification": "high-risk", "wave": 5, "status": "pending", "blockedBy": [240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]} + {"id": 240, "label": "T22", "subject": "M9-T22: Template tree search box", "classification": "small", "wave": 1, "status": "completed"}, + {"id": 241, "label": "CLI", "subject": "M9-CLI: cached-call retry/discard CLI group", "classification": "small", "wave": 1, "status": "completed"}, + {"id": 242, "label": "T28a", "subject": "M9-T28a: Strict expression-trigger kind — backend", "classification": "small", "wave": 1, "status": "completed"}, + {"id": 243, "label": "T28b", "subject": "M9-T28b: Strict trigger-kind — UI selector + CLI flag", "classification": "small", "wave": 1, "status": "completed", "blockedBy": [242]}, + {"id": 244, "label": "T23a", "subject": "M9-T23a: Folder sibling reorder — service + command + handler", "classification": "standard", "wave": 2, "status": "completed"}, + {"id": 245, "label": "T23b", "subject": "M9-T23b: Folder reorder + root context menu — UI", "classification": "standard", "wave": 2, "status": "completed", "blockedBy": [240, 244]}, + {"id": 246, "label": "T25", "subject": "M9-T25: Connection live-status indicators", "classification": "standard", "wave": 2, "status": "completed"}, + {"id": 247, "label": "T24a", "subject": "M9-T24a: Move data connection between sites — command + handler + guards", "classification": "high-risk", "wave": 3, "status": "completed"}, + {"id": 248, "label": "T24b", "subject": "M9-T24b: Move connection — UI dialog + action", "classification": "standard", "wave": 3, "status": "completed", "blockedBy": [247, 246]}, + {"id": 249, "label": "T32a", "subject": "M9-T32a: SharedSchema entity + EF config + migration + repo", "classification": "high-risk", "wave": 3, "status": "completed"}, + {"id": 250, "label": "T32b", "subject": "M9-T32b: JSON Schema $ref resolver + deploy-time validation", "classification": "high-risk", "wave": 3, "status": "completed", "blockedBy": [249]}, + {"id": 251, "label": "T32c", "subject": "M9-T32c: Schema library — CRUD commands + handlers + Central UI page", "classification": "high-risk", "wave": 4, "status": "completed", "blockedBy": [249, 250]}, + {"id": 252, "label": "T30", "subject": "M9-T30: Schema-driven nested value-entry forms", "classification": "standard", "wave": 4, "status": "completed", "blockedBy": [250]}, + {"id": 253, "label": "T31", "subject": "M9-T31: Monaco JSON-Schema hover/completion", "classification": "standard", "wave": 4, "status": "completed", "blockedBy": [250, 252]}, + {"id": 254, "label": "T26a", "subject": "M9-T26a: Inheritance resolve service + query command", "classification": "high-risk", "wave": 4, "status": "completed"}, + {"id": 255, "label": "T26b", "subject": "M9-T26b: TemplateEdit — full inherited set + staleness banner", "classification": "standard", "wave": 4, "status": "completed", "blockedBy": [254]}, + {"id": 256, "label": "INT", "subject": "M9-INT: Integration — build, docker, Playwright, smoke, end-to-end trace", "classification": "high-risk", "wave": 5, "status": "completed", "blockedBy": [240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]} ], "serializationPoints": { "ManagementActor.cs+ManagementCommandRegistry.cs": [244, 247, 251, 254], @@ -30,5 +30,6 @@ "TemplateEdit.razor": [243, 255], "ParameterValueForm.razor/value-entry-surface": [252, 253] }, - "lastUpdated": "2026-06-18" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-19-sms-notifications.md.tasks.json b/docs/plans/2026-06-19-sms-notifications.md.tasks.json index 4bc4c9aa..cb778d6f 100644 --- a/docs/plans/2026-06-19-sms-notifications.md.tasks.json +++ b/docs/plans/2026-06-19-sms-notifications.md.tasks.json @@ -1,17 +1,18 @@ { "planPath": "docs/plans/2026-06-19-sms-notifications.md", "tasks": [ - {"id": 299, "subject": "S1: Commons foundation — NotificationType.Sms + recipient phone + SmsConfiguration + repo iface", "status": "pending"}, - {"id": 300, "subject": "S2: Config-DB — EF mappings + AuthToken encryption + idempotent migration", "status": "pending", "blockedBy": [299]}, - {"id": 303, "subject": "S5: Management — list-command Type + SMS-config commands/handlers", "status": "pending", "blockedBy": [299]}, - {"id": 301, "subject": "S3: SmsNotificationDeliveryAdapter (Twilio REST) + classifier + options + DI + tests", "status": "pending", "blockedBy": [299, 300]}, - {"id": 302, "subject": "S4: NotificationOutboxActor ingest type-stamping from list", "status": "pending", "blockedBy": [299]}, - {"id": 304, "subject": "S6: CLI — list --type/--phones + notification sms group", "status": "pending", "blockedBy": [303]}, - {"id": 305, "subject": "S7: Central UI — NotificationListForm adapter-gated Type selector + per-type recipient input", "status": "pending", "blockedBy": [299, 303]}, - {"id": 306, "subject": "S8: Central UI — NotificationLists Type column", "status": "pending", "blockedBy": [299]}, - {"id": 307, "subject": "S9: Central UI — SMS configuration page (/notifications/sms)", "status": "pending", "blockedBy": [300, 303]}, - {"id": 308, "subject": "S10: Transport — recipient PhoneNumber DTO + SmsConfigDto round-trip", "status": "pending", "blockedBy": [299]}, - {"id": 309, "subject": "S11: INT — build, drift, docker, Playwright, live smoke, docs, whole-branch review", "status": "pending", "blockedBy": [300, 301, 302, 303, 304, 305, 306, 307, 308]} + {"id": 299, "subject": "S1: Commons foundation — NotificationType.Sms + recipient phone + SmsConfiguration + repo iface", "status": "completed"}, + {"id": 300, "subject": "S2: Config-DB — EF mappings + AuthToken encryption + idempotent migration", "status": "completed", "blockedBy": [299]}, + {"id": 303, "subject": "S5: Management — list-command Type + SMS-config commands/handlers", "status": "completed", "blockedBy": [299]}, + {"id": 301, "subject": "S3: SmsNotificationDeliveryAdapter (Twilio REST) + classifier + options + DI + tests", "status": "completed", "blockedBy": [299, 300]}, + {"id": 302, "subject": "S4: NotificationOutboxActor ingest type-stamping from list", "status": "completed", "blockedBy": [299]}, + {"id": 304, "subject": "S6: CLI — list --type/--phones + notification sms group", "status": "completed", "blockedBy": [303]}, + {"id": 305, "subject": "S7: Central UI — NotificationListForm adapter-gated Type selector + per-type recipient input", "status": "completed", "blockedBy": [299, 303]}, + {"id": 306, "subject": "S8: Central UI — NotificationLists Type column", "status": "completed", "blockedBy": [299]}, + {"id": 307, "subject": "S9: Central UI — SMS configuration page (/notifications/sms)", "status": "completed", "blockedBy": [300, 303]}, + {"id": 308, "subject": "S10: Transport — recipient PhoneNumber DTO + SmsConfigDto round-trip", "status": "completed", "blockedBy": [299]}, + {"id": 309, "subject": "S11: INT — build, drift, docker, Playwright, live smoke, docs, whole-branch review", "status": "completed", "blockedBy": [300, 301, 302, 303, 304, 305, 306, 307, 308]} ], - "lastUpdated": "2026-06-19" + "lastUpdated": "2026-08-01", + "status": "2026-08-01 bookkeeping sync: statuses reconciled against merged code" } diff --git a/docs/plans/2026-06-26-deploy-config-notify-and-fetch-design.md b/docs/plans/2026-06-26-deploy-config-notify-and-fetch-design.md index 6658aaa9..98e68015 100644 --- a/docs/plans/2026-06-26-deploy-config-notify-and-fetch-design.md +++ b/docs/plans/2026-06-26-deploy-config-notify-and-fetch-design.md @@ -3,6 +3,8 @@ **Date:** 2026-06-26 · **Status:** APPROVED (design) · **Area:** Deployment Manager / Site Runtime / Cluster Communication **Fixes:** [`docs/known-issues/2026-06-26-deploy-config-exceeds-akka-frame-size.md`](../known-issues/2026-06-26-deploy-config-exceeds-akka-frame-size.md) +> **2026-08-01 bookkeeping sync:** the central→site ClusterClient hop described below is now **gRPC** (`SiteCommandService`/`GrpcSiteTransport`) after the 2026-07-22 ClusterClient→gRPC migration — the notify-and-fetch behavior described here is unchanged. + ## 1. Problem The flattened instance config travels over Akka on **two** hops today, and both are bounded by Akka.Remote's default `maximum-frame-size` (**128 KB / `128000b`**). A large config (e.g. a 3rd composition of the same base template) silently breaks both: @@ -177,7 +179,7 @@ Smoke-tested on the docker cluster (rebuilt from this branch). Validated end-to- The smoke surfaced two real bugs in the reconciliation path (missed by unit/integration tests because those didn't have a second concurrent node or a lingering expired row), both fixed: 1. **Concurrent-gap omit** — when two nodes were concurrently missing the same instance, the second node's `StagePendingIfAbsentAsync` returned false and the handler *omitted* the item, leaving that node unhealed. Fix: on false, return the **existing** pending row's deploymentId + token (multi-use within TTL) so all concurrently-missing nodes heal in the same round. -2. **Expired pending row blocks self-heal** — `StagePendingIfAbsentAsync` checked existence by `InstanceId` ignoring expiry, so an expired-but-unpurged row (the periodic purge is still a deferred TODO) blocked a fresh stage *and* would collide with the snapshot's reused `DeploymentId` on the unique index. Fix: **expiry-aware staging** — delete expired rows for the instance first, then check only live rows; `GetPendingDeploymentByInstanceIdAsync` filters by expiry. This also opportunistically cleans expired rows, reducing reliance on the deferred periodic purge. +2. **Expired pending row blocks self-heal** — `StagePendingIfAbsentAsync` checked existence by `InstanceId` ignoring expiry, so an expired-but-unpurged row (the periodic purge was a deferred TODO at the time — **RESOLVED 2026-08-01 bookkeeping sync: `PendingDeploymentPurgeActor` shipped in PLAN-04; deferred-work register row #20 closed**) blocked a fresh stage *and* would collide with the snapshot's reused `DeploymentId` on the unique index. Fix: **expiry-aware staging** — delete expired rows for the instance first, then check only live rows; `GetPendingDeploymentByInstanceIdAsync` filters by expiry. This also opportunistically cleans expired rows, reducing reliance on the deferred periodic purge. ## 12. Affected files (for the plan) diff --git a/docs/plans/2026-07-10-aggregated-live-alarm-stream-plan.md b/docs/plans/2026-07-10-aggregated-live-alarm-stream-plan.md index f5d28893..00aa8992 100644 --- a/docs/plans/2026-07-10-aggregated-live-alarm-stream-plan.md +++ b/docs/plans/2026-07-10-aggregated-live-alarm-stream-plan.md @@ -1,6 +1,9 @@ # Plan: Aggregated Live Alarm Stream for Alarm Summary (deferred #10) -**Status:** Draft plan (not yet executed) — 2026-07-10 +**Status:** **Delivered 2026-07-10** — shipped as designed (`SiteAlarmAggregatorActor`, +`ISiteAlarmLiveCache`, the `SubscribeSite` streaming RPC, and +`AlarmSummaryService.BuildFromLiveAlarms` are all in `src/`). The 15 s poll is retained as the +NotReporting authority behind the live stream, per the design below. **Register row:** `docs/plans/2026-07-08-deferred-work-register.md` #10 **Revisit trigger that fired this plan:** *"Alarm Summary latency complaints or >~50 instances/site."* **Owning component:** Central UI (#9) + Central–Site Communication (#5); no new component. diff --git a/docs/plans/2026-07-10-kpi-history-hourly-rollups-plan.md b/docs/plans/2026-07-10-kpi-history-hourly-rollups-plan.md index 290a1bc1..ed8ab710 100644 --- a/docs/plans/2026-07-10-kpi-history-hourly-rollups-plan.md +++ b/docs/plans/2026-07-10-kpi-history-hourly-rollups-plan.md @@ -1,6 +1,9 @@ # Plan: KPI History Hourly Rollups (deferred #22) -**Status:** Draft plan (not yet executed) — 2026-07-10 +**Status:** **Delivered 2026-07-10** — shipped as designed: the `KpiRollupHourly` entity + +EF migration `20260710153953_AddKpiRollupHourlyTable`, `KpiMetricAggregationCatalog` +(per-metric gauge-vs-rate fold), and the raw-vs-rollup query routing on `RollupThresholdHours` +are all in `src/`. **Register row:** `docs/plans/2026-07-08-deferred-work-register.md` #22 **Revisit trigger that fired this plan:** *"KpiSample query latency on dashboards."* **Owning component:** KPI History (#26); touches Configuration Database (#17), diff --git a/docs/plans/2026-07-19-localdb-adoption-phase1.md.tasks.json b/docs/plans/2026-07-19-localdb-adoption-phase1.md.tasks.json index 89584f89..8e322620 100644 --- a/docs/plans/2026-07-19-localdb-adoption-phase1.md.tasks.json +++ b/docs/plans/2026-07-19-localdb-adoption-phase1.md.tasks.json @@ -191,5 +191,5 @@ } ], "lastUpdated": "2026-07-19", - "phase1Status": "COMPLETE - all 15 tasks done, live gate PASS. Branch feat/localdb-phase1 NOT merged/pushed." + "phase1Status": "COMPLETE - all 15 tasks done, live gate PASS. MERGED to main as 28ca04d7 ('LocalDb adoption Phase 1 + 2: consolidate the site database, delete the bespoke replicators (#23)') - Phase 1 and Phase 2 landed together in that one merge." } \ No newline at end of file diff --git a/docs/plans/2026-07-19-localdb-adoption-phase2.md.tasks.json b/docs/plans/2026-07-19-localdb-adoption-phase2.md.tasks.json index e13c19c6..8803467b 100644 --- a/docs/plans/2026-07-19-localdb-adoption-phase2.md.tasks.json +++ b/docs/plans/2026-07-19-localdb-adoption-phase2.md.tasks.json @@ -89,7 +89,7 @@ {"id": 18, "subject": "Task 18: Two-node convergence suite for the Phase 2 tables", "status": "completed", "classification": "high-risk", "blockedBy": [17], "note": "DEVIATION: landed as a NEW file LocalDbPhase2ConvergenceTests.cs rather than extending Phase 1's LocalDbSitePairConvergenceTests.cs, and drives the REAL SiteStorageService instead of hand-written SQL - possible only post-cutover, and what makes the cascade scenario test the shipped transaction rather than a re-creation of it. Scenario 4 was retargeted onto shared_scripts/external_systems/static_attribute_overrides because the plan's version overlapped LocalDbConfigConvergenceTests' N1 scenario almost exactly; the union-survives property is per-table, so re-proving it on untouched tables is the non-redundant half. Cascade test carries a never-removed control instance (absence assertions otherwise cannot distinguish 'cascade converged' from 'node B lost these tables'). Non-vacuity PROVEN as mandated: with the 8 RegisterReplicated calls commented out, 4 failed / 0 passed; restored, 20/20 across the three LocalDb suites.", "commit": "15013156"}, {"id": 19, "subject": "Task 19: Rig configuration (MaxOplogRows/MaxOplogAge + MaxBatchSize per D6)", "status": "completed", "classification": "small", "blockedBy": [17], "note": "MaxBatchSize 500->16 (D6: row-count batching x ~70 KB production config_json vs the 4 MB gRPC cap; 16 => ~1.1 MB worst case). MaxOplogRows 1M->250,000 and MaxOplogAge 7d->2d from the soak's 0.80 rows/sec (~69k/day). Tighter-than-default is SAFE because a cap breach prunes + sets needs_snapshot (graceful snapshot resync), not data loss - the Task 1 finding that the plan's stop condition was weaker than written. site-b/site-c left unreplicated so default-OFF stays proven side by side.", "commit": "921edab4"}, {"id": 20, "subject": "Task 20: Live gate on the docker rig", "status": "completed", "classification": "high-risk", "blockedBy": [18, 19], "note": "ALL 10 CHECKS PASS. Evidence: docs/plans/2026-07-19-localdb-phase2-live-gate.md. Key blocker found and fixed mid-run: external systems reach a site ONLY via ArtifactDeploymentService, which `instance deploy` never invokes - `deploy artifacts` was needed both to deliver the probe harness and to propagate the owed ExternalSystemDefinitions restore. THREE METHOD CORRECTIONS to the plan: (1) its instruction to run DB checks host-side against the bind mounts is UNSAFE - host sqlite3 poisons the container WAL; copy the db/-wal/-shm triplet and query the copy. (2) `docker exec ... curl` cannot scrape metrics (no curl in aspnet:10.0) and with 2>/dev/null the failure is silent - it nearly became a false 'metrics missing' finding; use a network-sharing curl sidecar. (3) checks needing S&F load need CachedCall, not Call. CAVEATS recorded not glossed: check 2's zero-count is vacuous alone (legacy source was also empty) and rests on the ABSENCE of CDC triggers; check 7's native_alarm_state leg was empty live and is covered only offline; check 10's sampling is coarse and the real rise/drain evidence comes from check 6's 0->4->0.", "commit": "158e79bb"}, - {"id": 21, "subject": "Task 21: Documentation truth pass", "status": "completed", "classification": "standard", "blockedBy": [20], "note": "Both CLAUDE.md files + Component-StoreAndForward.md:83 (normative resync paragraph rewritten for CDC, stating the duplicate-delivery bound explicitly: limited to messages the OLD primary delivered whose status change had not yet replicated when the gate flipped - one flush interval plus in-flight ack, and it does NOT grow with backlog depth or absence duration) + components/{StoreAndForward,SiteRuntime,Host}.md + the frame-size known-issue (amended: Phase 2 deleted notify-and-fetch itself, so the 128KB Akka frame constraint is gone from the intra-site hop entirely; successor ceiling is the 4MB gRPC cap via MaxBatchSize, and note the failure mode differs - oversized gRPC is REJECTED, not silently dropped) + deployment topology-guide.md and installation-guide.md (D5 stop-both-together, D2 TombstoneRetention resurrection bound). DoD closed: build 0 warnings, all 10 suites green (3509 tests, 0 failures). DoD grep nuance recorded in the plan: 4 matches remain in src/ and are all deliberate COMMENT prose explaining what was deleted - a literal 'no matches' would delete the explanations that stop someone re-introducing the old design.", "commit": null} + {"id": 21, "subject": "Task 21: Documentation truth pass", "status": "completed", "classification": "standard", "blockedBy": [20], "note": "Both CLAUDE.md files + Component-StoreAndForward.md:83 (normative resync paragraph rewritten for CDC, stating the duplicate-delivery bound explicitly: limited to messages the OLD primary delivered whose status change had not yet replicated when the gate flipped - one flush interval plus in-flight ack, and it does NOT grow with backlog depth or absence duration) + components/{StoreAndForward,SiteRuntime,Host}.md + the frame-size known-issue (amended: Phase 2 deleted notify-and-fetch itself, so the 128KB Akka frame constraint is gone from the intra-site hop entirely; successor ceiling is the 4MB gRPC cap via MaxBatchSize, and note the failure mode differs - oversized gRPC is REJECTED, not silently dropped) + deployment topology-guide.md and installation-guide.md (D5 stop-both-together, D2 TombstoneRetention resurrection bound). DoD closed: build 0 warnings, all 10 suites green (3509 tests, 0 failures). DoD grep nuance recorded in the plan: 4 matches remain in src/ and are all deliberate COMMENT prose explaining what was deleted - a literal 'no matches' would delete the explanations that stop someone re-introducing the old design.", "commit": "7b5a5a6f"} ], "knownFlakes": [ { @@ -102,5 +102,5 @@ } ], "lastUpdated": "2026-07-20", - "phase2Status": "UNBLOCKED - Task 1 gate CLOSED (verdict PROCEED) and Task 2 DONE, 2026-07-20. Task 1's original STOP verdict is SUPERSEDED: the 'Phase 1 disk I/O defect' was OBSERVER-INDUCED (host-side sqlite3 against live bind-mounted WAL files resets the WAL across virtiofs and permanently poisons the container's connections) - NOT a product defect. See docs/known-issues/2026-07-20-localdb-disk-io-error-under-load.md. Both earlier isolation claims were confounded: one sampling pass had already poisoned BOTH nodes, and a poisoned standby looks healthy only because it issues almost no statements; 'LocalDb-specific' was sampling-selection bias (only the LocalDb file had ever been host-read). Clean re-run 2026-07-20 with the copy-based snap() helper on restarted nodes, 6 consecutive 60s intervals: sf_messages 48 rows/min = 0.80 rows/sec dead steady, retry-UPDATE rate 0/sec (SUM(retry_count) flat at 200), oplog 0, native_alarm_state 0, max payload_json 76 B, max config_json 721 B, ZERO SQLite errors across 30 min, both site-a nodes converged at 3564 rows. Honest gap: 0.80/s is ~1.6% of the 50/s ceiling and the retry-UPDATE path was never exercised - acceptable because that ceiling is structural (SweepBatchLimit/RetryTimerInterval), not empirical. Rig config rows (721 B) are NOT representative; D6 sizing rests on the documented ~60-70 KB production config_json. Plan-premise corrections stand: D6 (MaxBatchSize 500 -> 16, the one firmly evidence-backed number; Task 19 sets it), D4 (alarm writes bounded by per-SourceReference coalescing at a 100 ms flush, NOT unbounded), sf_messages hard ceiling 50 rows/sec, oplog cap overrun = graceful snapshot resync (needs_snapshot), not data loss. NEXT: Wave 1 = Tasks 3 + 4, dispatchable in parallel (disjoint Files blocks). Tasks 3-21 untouched; no plan code written yet. Rig cleanup still owed before Task 20: restore ExternalSystemDefinitions id 1 to http://scadabridge-restapi:5200 (currently http://127.0.0.1:9), and remove SoakGenerator template 2021 + instances soakgen-1..4 (ids 5-8), still deployed and generating load." + "phase2Status": "COMPLETE - all 21 tasks done, live gate PASS (docs/plans/2026-07-19-localdb-phase2-live-gate.md), MERGED to main as 28ca04d7 ('LocalDb adoption Phase 1 + 2 ... (#23)'), which carried Phase 1 in the same merge. History below retained for context. Task 1 gate CLOSED (verdict PROCEED) and Task 2 DONE, 2026-07-20. Task 1's original STOP verdict is SUPERSEDED: the 'Phase 1 disk I/O defect' was OBSERVER-INDUCED (host-side sqlite3 against live bind-mounted WAL files resets the WAL across virtiofs and permanently poisons the container's connections) - NOT a product defect. See docs/known-issues/2026-07-20-localdb-disk-io-error-under-load.md. Both earlier isolation claims were confounded: one sampling pass had already poisoned BOTH nodes, and a poisoned standby looks healthy only because it issues almost no statements; 'LocalDb-specific' was sampling-selection bias (only the LocalDb file had ever been host-read). Clean re-run 2026-07-20 with the copy-based snap() helper on restarted nodes, 6 consecutive 60s intervals: sf_messages 48 rows/min = 0.80 rows/sec dead steady, retry-UPDATE rate 0/sec (SUM(retry_count) flat at 200), oplog 0, native_alarm_state 0, max payload_json 76 B, max config_json 721 B, ZERO SQLite errors across 30 min, both site-a nodes converged at 3564 rows. Honest gap: 0.80/s is ~1.6% of the 50/s ceiling and the retry-UPDATE path was never exercised - acceptable because that ceiling is structural (SweepBatchLimit/RetryTimerInterval), not empirical. Rig config rows (721 B) are NOT representative; D6 sizing rests on the documented ~60-70 KB production config_json. Plan-premise corrections stand: D6 (MaxBatchSize 500 -> 16, the one firmly evidence-backed number; Task 19 sets it), D4 (alarm writes bounded by per-SourceReference coalescing at a 100 ms flush, NOT unbounded), sf_messages hard ceiling 50 rows/sec, oplog cap overrun = graceful snapshot resync (needs_snapshot), not data loss. Tasks 3-21 were subsequently executed and all landed. Rig cleanup that was owed before Task 20 is DONE: ExternalSystemDefinitions id 1 restored to http://scadabridge-restapi:5200 (propagation to both site nodes confirmed), and SoakGenerator template 2021 + instances soakgen-1..4 deleted - see 'Rig state as left' in the live-gate doc." } diff --git a/docs/plans/2026-07-19-localdb-phase2-live-gate.md b/docs/plans/2026-07-19-localdb-phase2-live-gate.md index 1ee79c04..15058945 100644 --- a/docs/plans/2026-07-19-localdb-phase2-live-gate.md +++ b/docs/plans/2026-07-19-localdb-phase2-live-gate.md @@ -195,7 +195,9 @@ was down and back to 0 after rejoin. - **Deleting an instance orphans its buffered messages.** Removing the `soakgen-*` instances left their 11,804 `sf_messages` with no tracking snapshot, producing a continuous `Cached-telemetry drain: no tracking snapshot for …` warning flood. Also pre-existing, and worth - a cleanup path. + a cleanup path. **RESOLVED** by `62cddcfa` ("let an unresolvable cached row leave the drain + queue", merged `663d03e8`) — an unresolvable row is now dropped from the drain queue instead of + being retried forever, so the hot loop and the warning flood are gone. ## Rig state as left diff --git a/docs/plans/2026-07-19-localdb-phase2-soak.md b/docs/plans/2026-07-19-localdb-phase2-soak.md index 58fa1460..237022a6 100644 --- a/docs/plans/2026-07-19-localdb-phase2-soak.md +++ b/docs/plans/2026-07-19-localdb-phase2-soak.md @@ -72,7 +72,11 @@ Sustained rate observed: ~**2.9 HTTP attempts/sec** (688–864 connection-refuse image does not implement. After `reseed.sh` drops the volume (`docker compose down -v`), nothing recreates `ScadaBridgeConfig` or the `scadabridge_app` login, so `reseed.sh` hangs forever on its "Waiting for setup.sql to create ScadaBridgeConfig" poll. Worked around by - applying the three init scripts by hand. **NOT yet fixed in the repo.** + applying the three init scripts by hand. **Since FIXED in the repo:** `infra/reseed.sh` now + applies all three scripts explicitly via `sqlcmd` (`mssql/setup.sql`, + `mssql/machinedata_seed.sql`, `mssql/setup-env2.sql`), with a comment recording that the + `mssql/server` image has no initdb hook; the `/docker-entrypoint-initdb.d/` mounts in + `infra/docker-compose.yml` are documented as **informational only**. --- diff --git a/docs/plans/2026-07-21-auto-down-availability-decision.md b/docs/plans/2026-07-21-auto-down-availability-decision.md index f178769a..623f40ca 100644 --- a/docs/plans/2026-07-21-auto-down-availability-decision.md +++ b/docs/plans/2026-07-21-auto-down-availability-decision.md @@ -97,12 +97,17 @@ mis-explanation in the repo ("the alone-oldest is dead and cannot down itself"). ## Residual operational notes -- **Seed-node bootstrap constraint still applies to boot-alone**: only the first seed - may self-form a cluster. Auto-down removes the active-crash outage (the survivor never - restarts), but a node that must BOOT alone while its peer is dead (cold start of only - the non-first-seed VM, or the survivor crashing while the peer is still down) still - waits in `InitJoin` for its peer. Operator recovery unchanged (restart first seed, or - self-first seed override). +- ~~**Seed-node bootstrap constraint still applies to boot-alone**~~ — **SUPERSEDED + (2026-07-22), no longer a residual.** As written: only the first seed may self-form a + cluster, so a node that must BOOT alone while its peer is dead (cold start of only the + non-first-seed VM, or the survivor crashing while the peer is still down) waits in + `InitJoin` for its peer. This was **closed** by the self-first seed-node ordering change + (`docs/plans/2026-07-22-selfform-fallback-and-manual-failover.md`): every node now lists + ITSELF first in `seed-nodes`, so any node can cold-start alone and become operational + unattended, and `StartupValidator` fails the boot if the ordering is broken — regression + coverage in `SelfFirstSeedBootstrapTests`. The residual cost of self-first-on-both (a + *truly simultaneous* cold start racing `FirstSeedNodeProcess` on both nodes) is in turn + covered by the opt-in `ScadaBridge:Cluster:BootstrapGuard` (Gitea #33, default OFF). - Monitoring already surfaces dual-active if it ever happens: both nodes report `IsActive` in heartbeats / both `/health/active` = 200 — the Health dashboard shows two Primaries. diff --git a/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md b/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md index 6bbed433..51807e40 100644 --- a/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md +++ b/docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md @@ -118,6 +118,9 @@ Server's retrying strategy refuses to run a split query inside a caller's transa **bundle import fails against real MS SQL**. The fix is the one the exception names: wrap the transaction in `Database.CreateExecutionStrategy().ExecuteAsync(...)`. +> **FIXED (Gitea #28, commit `c4dcd9bc`)** — bundle import now runs inside the execution +> strategy, with a regression test. + Why the whole unit/integration suite is green on it: those tests use the in-memory EF provider, which has no retrying execution strategy — and `BeginTransactionAsync` is a no-op there. The comment directly above line 1298 documents that divergence without drawing the conclusion. Only @@ -134,13 +137,18 @@ This has been failing since 2026-07-10, and it matters more than a red line: eve the toast assertion — including **the secret-non-leak assertion that the Auth Token value never reaches the page HTML** — has not executed since. Fix is a valid 32-hex SID in the fixture. +> **FIXED (Gitea #29, commit `8524a7f7`)** — the fixture now uses a valid 32-hex Twilio SID, so +> the create path (and the secret-non-leak assertion behind it) executes again. + ### Not covered by this gate - Streaming subscriptions were exercised in-process (TestServer), not over the rig. The interceptor is path-scoped, not method-scoped, so the rig's `PullAuditEvents` evidence covers the same code path — but a live `SubscribeInstance` under load is untested here. - Key **rotation** on a live pair. -- `docker-env2` was updated with its own key but not redeployed or gated. +- `docker-env2` was updated with its own key but not redeployed or gated. — **CLOSED + (Gitea #31, commit `e0f105c3`)**: `docker-env2` was rebuilt on the PSK build and gated, + **PASS 3/3**; see the "Env2 gate" section at the end of this document. --- diff --git a/docs/plans/2026-07-22-clusterclient-to-grpc-plan.md.tasks.json b/docs/plans/2026-07-22-clusterclient-to-grpc-plan.md.tasks.json index 1ba8dda8..bbf8c184 100644 --- a/docs/plans/2026-07-22-clusterclient-to-grpc-plan.md.tasks.json +++ b/docs/plans/2026-07-22-clusterclient-to-grpc-plan.md.tasks.json @@ -132,11 +132,12 @@ "id": "P1A.DoD", "phase": "1A", "subject": "1A DoD: rig site-a on Grpc proves all 5 site->central paths while site-b/c stay Akka; PR merged before 1B", - "status": "pending", + "status": "completed", "activeForm": "Verifying the 1A DoD", "blockedBy": [ "T1A.4" - ] + ], + "notes": "PASS - ticked in the plan MD. Rig site-a on CentralTransport=Grpc proved the site->central paths (heartbeat/health/reconcile + Akka coexistence on site-b/c); PR #26 merged as aa60f438. Notification/audit deferred to the Phase 2 soak (no deployed instance at the time). The rig caught and fixed a central :5000 HTTP-drop regression (0e162cb2). Gate: docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md." }, { "id": "T1B.1", @@ -187,12 +188,13 @@ "id": "P1B.DoD", "phase": "1B", "subject": "1B DoD: rig central on Grpc for site-a proves full command matrix incl. standby parked retry; rebased on 1A; PR merged", - "status": "pending", + "status": "completed", "activeForm": "Verifying the 1B DoD", "blockedBy": [ "T1B.4", "P1A.DoD" - ] + ], + "notes": "PASS (proportionate) - ticked in the plan MD (2fa5e93c). Rig central on SiteTransport=Grpc proved central->site rides the authenticated SiteCommandService for all 3 sites (ExecuteQuery/ExecuteParked/ExecuteLifecycle -> 200, per-site PSK, 0 auth failures); rebased on 1A. Instance-dependent commands (tag ops/lifecycle/standby parked retry) + TriggerSiteFailover deferred to Phase 3 (no deployed instance / destructive UI-only); command-plane coexistence not expressible because SiteTransport is central-wide. Phases 2-5 completed downstream. Gate: docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md." }, { "id": "P2", diff --git a/docs/plans/2026-07-23-otopcua-v3-dual-namespace-cutover-scope.md b/docs/plans/2026-07-23-otopcua-v3-dual-namespace-cutover-scope.md index 2dbb7a47..747d3d9a 100644 --- a/docs/plans/2026-07-23-otopcua-v3-dual-namespace-cutover-scope.md +++ b/docs/plans/2026-07-23-otopcua-v3-dual-namespace-cutover-scope.md @@ -1,9 +1,15 @@ # OtOpcUa v3.0 dual-namespace cutover — scoping (Gitea #14) -**Date:** 2026-07-23 · **Status:** SCOPING (decisions needed — see §6) · **Tracked:** Gitea -[#14](https://gitea.dohertylan.com/dohertj2/ScadaBridge/issues/14) · **Area:** Data Connection -Layer (OPC UA adapter) · **Upstream:** OtOpcUa v3.0 (merged `master` 2026-07-16, PR #472, merge -`ec6598ce`); design `~/Desktop/OtOpcUa/docs/plans/2026-07-15-raw-uns-two-subtree-v3-design.md`. +**Date:** 2026-07-23 · **Status:** **DECIDED — D-1/D-2/D-3 all resolved inline in §6; phase 2 +(`nsu=` hardening + browse UX, items D/E/F) SHIPPED** (`0eb44314`, `97afa84f`, `e04c2617`; this +scope doc + the phase-2 plan committed as `266f001a`). Live gates PASS: raw-path +`2026-07-23-otopcua-v3-raw-path-live-gate.md`, alarms +`2026-07-23-otopcua-v3-alarm-bc-live-gate.md`. Only **item A** (re-author legacy `ns=2` +bindings — data/operational, greenfield) remains open. · **Tracked:** Gitea +[#14](https://gitea.dohertylan.com/dohertj2/ScadaBridge/issues/14) (CLOSED 2026-07-24) · **Area:** +Data Connection Layer (OPC UA adapter) · **Upstream:** OtOpcUa v3.0 (merged `master` 2026-07-16, +PR #472, merge `ec6598ce`); design +`~/Desktop/OtOpcUa/docs/plans/2026-07-15-raw-uns-two-subtree-v3-design.md`. ## 1. Headline @@ -74,7 +80,7 @@ Grounded in the DCL code (`src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/`): |---|------|------|---------------|------------| | A | **Legacy `ns=` bindings collide with v3.** v2's sole custom namespace and v3's `raw` both land at `ns=2`, so a stored `ns=2;s=…` resolves **without error** but now means a raw-tree node. Every stored binding must be re-authored (or migrated) to the new address space, ideally in `nsu=` form. | **Data** | `TemplateAttribute.DataSourceReference`, `InstanceConnectionBinding.DataSourceReferenceOverride` (config DB); heartbeat `TagPath`; Transport bundles | High (inspection) | | B | **Native-alarm dedup across the raw+uns fan-out.** `RealOpcUaClient.HandleAlarmEvent` keys on RawPath+ConditionName with **no `ConditionId`-based dedup**; it assumes one condition arrives on one feed. If a v3 server delivers the same condition through two notifier paths into the same feed, two `EventFieldList`s each call `onTransition`. Must verify the Server-object aggregate feed collapses to one copy (the v3 doc says the SDK dedups Server-object subscribers, so this *should* hold — but it is the biggest genuine gap and needs a live check; add ConditionId-based dedup only if the live server double-delivers). | **Code + live validation** | `Adapters/RealOpcUaClient.cs` (HandleAlarmEvent), `Adapters/OpcUaAlarmMapper.cs` | Medium (needs live v3) | -| C | **Alarm routing for UNS-bound sources (confirmed by D-1 = ingest both).** `DataConnectionActor` routes transitions by `SourceReference.StartsWith(bindingRef)`, but a v3 condition's `SourceName` is the **RawPath** regardless of subtree — so a **UNS-bound** alarm source won't `StartsWith`-match. Needs a routing change: resolve a UNS-bound source to its backing RawPath (via the browseable UNS→Raw `Organizes` reference) or map identities. Confirmed scope; validate the exact `SourceName`/`SourceNode` payload on the live rig before finalizing the mapping. | **Code + live validation** | `Actors/DataConnectionActor.cs` (alarm routing), `Adapters/RealOpcUaClient.cs` | Medium (needs live v3) | +| C | **~~Alarm routing for UNS-bound sources~~ — OBSOLETE (2026-07-23); no code change needed.** The premise below is superseded by Gitea #17: `OpcUaAlarmMapper.BuildIdentity` stamps `SourceObjectReference` = the **subscribed node reference**, and `DataConnectionActor` routes on *that*, not on the event `SourceName`. Confirmed live — alarm B/C live gate **PASS** 2026-07-23 (`2026-07-23-otopcua-v3-alarm-bc-live-gate.md`): a UNS-node-scoped binding received and routed the condition correctly, and `SourceName` turned out to be the ScriptedAlarmId (not the RawPath) without breaking anything. *Original scoping text:* **Alarm routing for UNS-bound sources (confirmed by D-1 = ingest both).** `DataConnectionActor` routes transitions by `SourceReference.StartsWith(bindingRef)`, but a v3 condition's `SourceName` is the **RawPath** regardless of subtree — so a **UNS-bound** alarm source won't `StartsWith`-match. Needs a routing change: resolve a UNS-bound source to its backing RawPath (via the browseable UNS→Raw `Organizes` reference) or map identities. Confirmed scope; validate the exact `SourceName`/`SourceNode` payload on the live rig before finalizing the mapping. | **Code + live validation** | `Actors/DataConnectionActor.cs` (alarm routing), `Adapters/RealOpcUaClient.cs` | Medium (needs live v3) | | D | **Browse / search UX now shows two subtrees.** `BrowseChildrenAsync` / `AddressSpaceSearch.SearchAsync` start at the standard Objects root and will enumerate **both** raw and uns as sibling roots. Functional, not breaking — but the picker shows two roots, the manual-entry placeholder still nudges `ns=2;s=…`, and the deeper raw hierarchy stresses `VisitedNodeCeiling`/`maxDepth`. | **UX** | `CentralUI/Components/Dialogs/NodeBrowserDialog.razor`, `IOpcUaClient` search caps | High (inspection) | | E | **`nsu=` hardening (optional, recommended).** The picker already emits `nsu=` via `ToDurable`, but the seam still **accepts** bare `ns=`. Decide whether to warn/reject bare `ns=` on save (closes A permanently) or leave it permissive. | **Code** | `OpcUaNodeReference`, binding validation, picker | High (inspection) | | F | **Doc drift.** `ns=` examples in code comments + `Component-DataConnectionLayer.md`; update to `nsu=`. Update the scadaproj umbrella index (cutover step 4). | **Docs** | doc comments, `docs/requirements/Component-DataConnectionLayer.md`, `../scadaproj/CLAUDE.md` | High | diff --git a/docs/plans/2026-07-23-otopcua-v3-nsu-hardening-and-browse-ux.md b/docs/plans/2026-07-23-otopcua-v3-nsu-hardening-and-browse-ux.md index 059b7b8d..bae5d912 100644 --- a/docs/plans/2026-07-23-otopcua-v3-nsu-hardening-and-browse-ux.md +++ b/docs/plans/2026-07-23-otopcua-v3-nsu-hardening-and-browse-ux.md @@ -250,9 +250,12 @@ git -C ../scadaproj add CLAUDE.md && git -C ../scadaproj commit -m "docs: note S Tracked in the scope doc (§5 phases 3–5, §7). Do not attempt without a re-seeded v3 server: -- **Item A** — re-author existing dev/test bindings across both subtrees via the picker (data, greenfield; D-3 = no migration code). -- **Item B** — native-alarm dedup across the raw+uns notifier fan-out; add `ConditionId` dedup **only if** the live server double-delivers. -- **Item C** — UNS-bound alarm routing: a v3 condition's `SourceName` is always the RawPath, so a UNS-bound source won't `StartsWith`-match — resolve UNS→Raw via the browseable `Organizes` reference. Build against the real `SourceName`/`SourceNode` payload captured on the rig. +- **Item A** — **STILL OPEN** — re-author existing dev/test bindings across both subtrees via the picker (data, greenfield; D-3 = no migration code). +- ~~**Item B**~~ — **CLOSED 2026-07-23** by the native-alarm B/C live gate (**PASS** — `docs/plans/2026-07-23-otopcua-v3-alarm-bc-live-gate.md`): the live v3 server does **not** double-deliver — one subscription per connection delivers each retained condition once, to the most-specific monitored notifier — so no `ConditionId` dedup was added. *(Original: native-alarm dedup across the raw+uns notifier fan-out; add `ConditionId` dedup only if the live server double-delivers.)* +- ~~**Item C**~~ — **CLOSED 2026-07-23** by the same gate (**PASS**): the premise is obsolete. ScadaBridge routes native alarms by the **subscribed node reference** (`OpcUaAlarmMapper.BuildIdentity` → `SourceObjectReference`), not the event `SourceName` (Gitea #17), so a UNS-bound source routes correctly with no code change. *(Original: a v3 condition's `SourceName` is always the RawPath, so a UNS-bound source won't `StartsWith`-match — resolve UNS→Raw via the browseable `Organizes` reference.)* + +Also still open (tracked, needs a live v3 rig): calibrating the address-space search `maxDepth: 6` +for the deeper raw hierarchy — see the note under Task 2. ## Verification (whole-plan, after Task 3) diff --git a/docs/plans/2026-07-23-otopcua-v3-raw-path-live-gate.md b/docs/plans/2026-07-23-otopcua-v3-raw-path-live-gate.md index 06c1733c..f8a0a987 100644 --- a/docs/plans/2026-07-23-otopcua-v3-raw-path-live-gate.md +++ b/docs/plans/2026-07-23-otopcua-v3-raw-path-live-gate.md @@ -63,14 +63,16 @@ client (`IEndpointDiscovery`). Commit `a1abbff7` on `fix/opcua-discovery-host-re ## Deferred (unchanged — need OtOpcUa-side authoring on the dev cluster) -- **Item A** — re-author legacy `ns=2` bindings to `nsu=` (data; the collision is confirmed real here). -- **Item B** — native-alarm dedup across raw+uns fan-out: **no native alarms exist** to drive it. -- **Item C** — UNS-bound → RawPath alarm routing: **no UNS nodes and no alarms exist** to drive it. +- **Item A** — **STILL OPEN** — re-author legacy `ns=2` bindings to `nsu=` (data; the collision is confirmed real here). +- ~~**Item B**~~ — **CLOSED 2026-07-23**, see below. +- ~~**Item C**~~ — **CLOSED 2026-07-23**, see below. To gate B/C, the OtOpcUa dev cluster needs UNS Equipment authored (populate ns=3, referencing raw tags) and at least one alarm-configured tag raising a Part-9 condition that fans to raw + equipment notifiers. +**That authoring was done the same day and B/C were gated: `docs/plans/2026-07-23-otopcua-v3-alarm-bc-live-gate.md` — PASS, no ScadaBridge code change needed.** B: one subscription per connection delivers each retained condition once, to the most-specific monitored notifier, so overlapping bindings do not double-mirror. C: the premise was obsolete — routing keys off the **subscribed node reference** (`OpcUaAlarmMapper.BuildIdentity` → `SourceObjectReference`), not the event `SourceName` (Gitea #17). Item **A** remains the only open #14 work. + ## Rig artifacts created (non-default rig state) - ScadaBridge site-a-a / site-a-b attached to `otopcua-dev_default` (dropped on next `docker/deploy.sh`). diff --git a/docs/plans/questions.md b/docs/plans/questions.md index cacf0a82..9dc319e8 100644 --- a/docs/plans/questions.md +++ b/docs/plans/questions.md @@ -50,5 +50,5 @@ | Q-P3C-2 | Max parked messages per remote query? | Paginated, 100 per page, consistent with Site Event Logging pattern. | 2026-03-16 | | Q-P3C-3 | Per-instance operation lock — in-memory or persisted? | In-memory. Released on failover. Site state query resolves any ambiguity. | 2026-03-16 | | Q-P4-1 | API key values — auto-generated or user-provided? | Auto-generated with copy-to-clipboard. User can regenerate. | 2026-03-16 | -| Q-P4-2 | Health dashboard refresh interval? | Updates on every report arrival (server push). No UI-side polling. | 2026-03-16 | +| Q-P4-2 | Health dashboard refresh interval? | Updates on every report arrival (server push). No UI-side polling. **Superseded (2026-08-01): as shipped, the Health dashboard refreshes on a 10s UI polling timer — the server-push answer recorded here was not the implemented behavior.** | 2026-03-16 | | Q-P4-3 | Area deletion — cascade or bottom-up? | Cascade delete child areas if no instances are assigned to any area in the subtree. | 2026-03-16 | diff --git a/docs/plans/requirements-traceability.md b/docs/plans/requirements-traceability.md index 64541dea..5f260128 100644 --- a/docs/plans/requirements-traceability.md +++ b/docs/plans/requirements-traceability.md @@ -1,5 +1,7 @@ # Requirements Traceability Matrix +> **Note (2026-08-01)**: The Status column is frozen at plan-generation time (March 2026). "Pending" means the phase plan had not yet been generated at that time — **not** that a requirement is unimplemented. See `phase-N-checklist.md` for delivery status. All phases have since been generated and delivered. + **Purpose**: Ensures every requirement from docs/requirements/HighLevelReqs.md, every REQ-* identifier, and every design constraint from CLAUDE.md and docs/requirements/Component-*.md maps to at least one work package in an implementation phase plan. Updated as plan documents are generated. **Traceability levels**: